fix: scrolls and context for deal prefilling
This commit is contained in:
@@ -6,7 +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";
|
||||
import { usePrefillDealContext } from "../../../pages/LeadsPage/contexts/PrefillDealContext.tsx";
|
||||
|
||||
type Props = {
|
||||
onClick: () => void;
|
||||
@@ -15,7 +15,7 @@ const CreateDealButton: FC<Props> = () => {
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
const [isTransitionEnded, setIsTransitionEnded] = useState(true);
|
||||
const queryClient = useQueryClient();
|
||||
const { prefillDeal, setPrefillDeal } = useDealPageContext();
|
||||
const { prefillDeal, setPrefillDeal } = usePrefillDealContext();
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -60,6 +60,7 @@ const CreateDealButton: FC<Props> = () => {
|
||||
queryKey: ["getDealSummaries"],
|
||||
});
|
||||
setIsCreating(false);
|
||||
setPrefillDeal(undefined);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -5,17 +5,18 @@ import { useForm } from "@mantine/form";
|
||||
import styles from "./CreateDealForm.module.css";
|
||||
import ClientAutocomplete from "../../Selects/ClientAutocomplete/ClientAutocomplete.tsx";
|
||||
import { DateTimePicker } from "@mantine/dates";
|
||||
import ShippingWarehouseAutocomplete from "../../Selects/ShippingWarehouseAutocomplete/ShippingWarehouseAutocomplete.tsx";
|
||||
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";
|
||||
import { usePrefillDealContext } from "../../../pages/LeadsPage/contexts/PrefillDealContext.tsx";
|
||||
|
||||
type Props = {
|
||||
onSubmit: (quickDeal: QuickDeal) => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
const CreateDealFrom: FC<Props> = ({ onSubmit, onCancel }) => {
|
||||
const { prefillOnOpen, prefillDeal } = useDealPageContext();
|
||||
const { prefillOnOpen, prefillDeal } = usePrefillDealContext();
|
||||
const form = useForm<QuickDeal>({
|
||||
initialValues: {
|
||||
name: "",
|
||||
|
||||
@@ -1,19 +1,9 @@
|
||||
import { createContext, Dispatch, FC, SetStateAction, useContext, useState } from "react";
|
||||
import { DealSchema, DealService } from "../../../client";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
import { createContext, FC, useContext, useState } from "react";
|
||||
import { DealSchema } from "../../../client";
|
||||
|
||||
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>(
|
||||
@@ -23,34 +13,10 @@ const useDealPageContextState = () => {
|
||||
const [selectedDeal, setSelectedDeal] = useState<DealSchema | undefined>(
|
||||
undefined
|
||||
);
|
||||
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,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
71
src/pages/LeadsPage/contexts/PrefillDealContext.tsx
Normal file
71
src/pages/LeadsPage/contexts/PrefillDealContext.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import { createContext, Dispatch, FC, SetStateAction, useContext, useState } from "react";
|
||||
import { DealSchema, DealService } from "../../../client";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
|
||||
type PrefillDealContextState = {
|
||||
prefillOpened: boolean;
|
||||
prefillOnClose: () => void;
|
||||
prefillOnOpen: () => void;
|
||||
|
||||
selectedPrefillDeal?: DealSchema;
|
||||
selectPrefillDeal: (dealId: number) => void;
|
||||
prefillDeal?: DealSchema;
|
||||
setPrefillDeal: Dispatch<SetStateAction<DealSchema | undefined>>;
|
||||
};
|
||||
|
||||
const PrefillDealContext = createContext<PrefillDealContextState | undefined>(
|
||||
undefined
|
||||
);
|
||||
const usePrefillDealContextState = () => {
|
||||
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 {
|
||||
prefillOpened,
|
||||
prefillOnClose,
|
||||
prefillOnOpen,
|
||||
|
||||
selectedPrefillDeal,
|
||||
selectPrefillDeal,
|
||||
prefillDeal,
|
||||
setPrefillDeal,
|
||||
};
|
||||
};
|
||||
|
||||
type PrefillDealContextProviderProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export const PrefillDealContextProvider: FC<PrefillDealContextProviderProps> = ({
|
||||
children,
|
||||
}) => {
|
||||
const state = usePrefillDealContextState();
|
||||
return (
|
||||
<PrefillDealContext.Provider value={state}>
|
||||
{children}
|
||||
</PrefillDealContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const usePrefillDealContext = () => {
|
||||
const context = useContext(PrefillDealContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
"usePrefillDealContext must be used within a PrefillDealContextProvider"
|
||||
);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@@ -1,15 +1,15 @@
|
||||
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";
|
||||
import { usePrefillDealContext } from "../../contexts/PrefillDealContext.tsx";
|
||||
|
||||
const DealPrefillDrawer: FC = () => {
|
||||
const { prefillOpened, prefillOnClose, selectedPrefillDeal, setPrefillDeal, prefillDeal } = useDealPageContext();
|
||||
const { prefillOpened, prefillOnClose, selectedPrefillDeal, setPrefillDeal, prefillDeal } = usePrefillDealContext();
|
||||
const { data, form } = usePrefillDeal();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
.container {
|
||||
display: flex;
|
||||
gap: rem(10);
|
||||
max-height: 95vh;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
@@ -20,6 +19,7 @@
|
||||
}
|
||||
|
||||
.deal-container-wrapper {
|
||||
width: 100%;
|
||||
border: dashed var(--item-border-size) var(--mantine-color-default-border);
|
||||
border-radius: var(--item-border-radius);
|
||||
padding: rem(10);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
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";
|
||||
import { usePrefillDealContext } from "../../../../contexts/PrefillDealContext.tsx";
|
||||
|
||||
const Preview: FC = () => {
|
||||
const { selectedPrefillDeal } = useDealPageContext();
|
||||
const { selectedPrefillDeal } = usePrefillDealContext();
|
||||
|
||||
const getTotalPrice = () => {
|
||||
if (!selectedPrefillDeal) return 0;
|
||||
@@ -28,27 +28,29 @@ const Preview: FC = () => {
|
||||
|
||||
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>
|
||||
<div className={styles["deal-container-wrapper"]}>
|
||||
<ScrollArea offsetScrollbars={"y"} w={"100%"}>
|
||||
<div style={{ height: "93vh" }}>
|
||||
<Skeleton visible={!selectedPrefillDeal}>
|
||||
<Title order={4} mb={18}>
|
||||
Общая стоимость всех услуг:{" "}
|
||||
{getTotalPrice().toLocaleString("ru")}₽
|
||||
</Title>
|
||||
|
||||
<DealServicesTable items={selectedPrefillDeal?.services} />
|
||||
<DealServicesTable items={selectedPrefillDeal?.services} />
|
||||
|
||||
<div className={styles["products-list"]}>
|
||||
{selectedPrefillDeal?.products.map(product => (
|
||||
<ProductPreview
|
||||
key={product.product.id}
|
||||
product={product}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className={styles["products-list"]}>
|
||||
{selectedPrefillDeal?.products.map(product => (
|
||||
<ProductPreview
|
||||
key={product.product.id}
|
||||
product={product}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Skeleton>
|
||||
</div>
|
||||
</Skeleton>
|
||||
</ScrollArea>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -16,7 +16,6 @@ const DealServicesTable: FC<Props> = ({ items }) => {
|
||||
<Flex
|
||||
direction={"column"}
|
||||
gap={rem(10)}
|
||||
h={"100%"}
|
||||
mb={10}>
|
||||
<Flex
|
||||
h={"100%"}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
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";
|
||||
import { usePrefillDealContext } from "../../../../../contexts/PrefillDealContext.tsx";
|
||||
|
||||
type Props = {
|
||||
items: DealSummary[];
|
||||
};
|
||||
|
||||
const DealsTable: FC<Props> = ({ items }) => {
|
||||
const { selectPrefillDeal } = useDealPageContext();
|
||||
const { selectPrefillDeal } = usePrefillDealContext();
|
||||
const columns = useDealsTableColumns();
|
||||
const defaultSorting = [{ id: "createdAt", desc: false }];
|
||||
|
||||
@@ -36,6 +36,9 @@ const DealsTable: FC<Props> = ({ items }) => {
|
||||
mantinePaginationProps: {
|
||||
showRowsPerPage: false,
|
||||
},
|
||||
enableStickyHeader: true,
|
||||
enableStickyFooter: true,
|
||||
mantineTableContainerProps: { style: { maxHeight: "81vh", height: "81vh" } },
|
||||
}
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -2,7 +2,7 @@ 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";
|
||||
import { usePrefillDealContext } from "../../../../../contexts/PrefillDealContext.tsx";
|
||||
|
||||
const useDealsTableColumns = () => {
|
||||
return useMemo<MRT_ColumnDef<DealSummary>[]>(
|
||||
@@ -13,7 +13,7 @@ const useDealsTableColumns = () => {
|
||||
size: 5,
|
||||
enableSorting: false,
|
||||
Cell: ({ row }) => {
|
||||
const { selectPrefillDeal, selectedPrefillDeal } = useDealPageContext();
|
||||
const { selectPrefillDeal, selectedPrefillDeal } = usePrefillDealContext();
|
||||
const checked = row.original.id === selectedPrefillDeal?.id;
|
||||
return (
|
||||
<Radio
|
||||
|
||||
@@ -21,6 +21,7 @@ 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";
|
||||
import { PrefillDealContextProvider } from "../contexts/PrefillDealContext.tsx";
|
||||
|
||||
enum DisplayMode {
|
||||
BOARD,
|
||||
@@ -317,101 +318,103 @@ export const LeadsPage: FC = () => {
|
||||
boxShadow: "none",
|
||||
}}>
|
||||
<DealPageContextProvider>
|
||||
<PageBlock style={{ flex: 0 }}>
|
||||
<Flex
|
||||
align={"center"}
|
||||
justify={"space-between"}>
|
||||
<PrefillDealContextProvider>
|
||||
<PageBlock style={{ flex: 0 }}>
|
||||
<Flex
|
||||
gap={rem(10)}
|
||||
direction={"column"}
|
||||
align={"center"}>
|
||||
<Text size={"xs"}>Вид</Text>
|
||||
<Flex gap={rem(10)}>
|
||||
<ActionIcon
|
||||
onClick={() =>
|
||||
setDisplayMode(DisplayMode.BOARD)
|
||||
}
|
||||
variant={
|
||||
displayMode === DisplayMode.BOARD
|
||||
? "filled"
|
||||
: "default"
|
||||
}>
|
||||
<IconMenuDeep
|
||||
style={{ rotate: "-90deg" }}
|
||||
/>
|
||||
</ActionIcon>
|
||||
<ActionIcon
|
||||
onClick={() =>
|
||||
setDisplayMode(DisplayMode.TABLE)
|
||||
}
|
||||
variant={
|
||||
displayMode === DisplayMode.TABLE
|
||||
? "filled"
|
||||
: "default"
|
||||
}>
|
||||
<IconMenu2 />
|
||||
</ActionIcon>
|
||||
align={"center"}
|
||||
justify={"space-between"}>
|
||||
<Flex
|
||||
gap={rem(10)}
|
||||
direction={"column"}
|
||||
align={"center"}>
|
||||
<Text size={"xs"}>Вид</Text>
|
||||
<Flex gap={rem(10)}>
|
||||
<ActionIcon
|
||||
onClick={() =>
|
||||
setDisplayMode(DisplayMode.BOARD)
|
||||
}
|
||||
variant={
|
||||
displayMode === DisplayMode.BOARD
|
||||
? "filled"
|
||||
: "default"
|
||||
}>
|
||||
<IconMenuDeep
|
||||
style={{ rotate: "-90deg" }}
|
||||
/>
|
||||
</ActionIcon>
|
||||
<ActionIcon
|
||||
onClick={() =>
|
||||
setDisplayMode(DisplayMode.TABLE)
|
||||
}
|
||||
variant={
|
||||
displayMode === DisplayMode.TABLE
|
||||
? "filled"
|
||||
: "default"
|
||||
}>
|
||||
<IconMenu2 />
|
||||
</ActionIcon>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<motion.div
|
||||
key={displayMode}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.2 }}>
|
||||
<div
|
||||
className={styles["top-panel"]}
|
||||
style={{
|
||||
display:
|
||||
displayMode === DisplayMode.TABLE
|
||||
? "flex"
|
||||
: "none",
|
||||
}}>
|
||||
<NumberInput
|
||||
min={1}
|
||||
placeholder={"Введите номер"}
|
||||
{...form.getInputProps("id")}
|
||||
hideControls
|
||||
/>
|
||||
<DealStatusSelect
|
||||
onClear={() =>
|
||||
form.setFieldValue("dealStatus", null)
|
||||
}
|
||||
clearable
|
||||
placeholder={"Выберите статус "}
|
||||
{...form.getInputProps("dealStatus")}
|
||||
/>
|
||||
<BaseMarketplaceSelect
|
||||
onClear={() =>
|
||||
form.setFieldValue("marketplace", null)
|
||||
}
|
||||
clearable
|
||||
placeholder={"Выберите маркетплейс"}
|
||||
{...form.getInputProps("marketplace")}
|
||||
/>
|
||||
<ClientSelectNew
|
||||
onClear={() =>
|
||||
form.setFieldValue("client", null)
|
||||
}
|
||||
clearable
|
||||
searchable
|
||||
placeholder={"Выберите клиента"}
|
||||
{...form.getInputProps("client")}
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
</Flex>
|
||||
<motion.div
|
||||
key={displayMode}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.2 }}>
|
||||
<div
|
||||
className={styles["top-panel"]}
|
||||
style={{
|
||||
display:
|
||||
displayMode === DisplayMode.TABLE
|
||||
? "flex"
|
||||
: "none",
|
||||
}}>
|
||||
<NumberInput
|
||||
min={1}
|
||||
placeholder={"Введите номер"}
|
||||
{...form.getInputProps("id")}
|
||||
hideControls
|
||||
/>
|
||||
<DealStatusSelect
|
||||
onClear={() =>
|
||||
form.setFieldValue("dealStatus", null)
|
||||
}
|
||||
clearable
|
||||
placeholder={"Выберите статус "}
|
||||
{...form.getInputProps("dealStatus")}
|
||||
/>
|
||||
<BaseMarketplaceSelect
|
||||
onClear={() =>
|
||||
form.setFieldValue("marketplace", null)
|
||||
}
|
||||
clearable
|
||||
placeholder={"Выберите маркетплейс"}
|
||||
{...form.getInputProps("marketplace")}
|
||||
/>
|
||||
<ClientSelectNew
|
||||
onClear={() =>
|
||||
form.setFieldValue("client", null)
|
||||
}
|
||||
clearable
|
||||
searchable
|
||||
placeholder={"Выберите клиента"}
|
||||
{...form.getInputProps("client")}
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
</Flex>
|
||||
</PageBlock>
|
||||
<PageBlock
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flex: 1,
|
||||
height: "100%",
|
||||
}}>
|
||||
{getBody()}
|
||||
</PageBlock>
|
||||
<DealEditDrawer />
|
||||
<DealPrefillDrawer />
|
||||
</PageBlock>
|
||||
<PageBlock
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flex: 1,
|
||||
height: "100%",
|
||||
}}>
|
||||
{getBody()}
|
||||
</PageBlock>
|
||||
<DealEditDrawer />
|
||||
<DealPrefillDrawer />
|
||||
</PrefillDealContextProvider>
|
||||
</DealPageContextProvider>
|
||||
</PageBlock>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user