fix: scrolls and context for deal prefilling

This commit is contained in:
2024-10-18 12:28:22 +04:00
parent c1b9031672
commit aa6f0364b5
11 changed files with 207 additions and 161 deletions

View File

@@ -6,7 +6,7 @@ import CreateDealFrom from "../CreateDealForm/CreateDealFrom.tsx";
import { DealService } from "../../../client"; import { DealService } from "../../../client";
import { useQueryClient } from "@tanstack/react-query"; import { useQueryClient } from "@tanstack/react-query";
import { dateWithoutTimezone } from "../../../shared/lib/date.ts"; 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 = { type Props = {
onClick: () => void; onClick: () => void;
@@ -15,7 +15,7 @@ const CreateDealButton: FC<Props> = () => {
const [isCreating, setIsCreating] = useState(false); const [isCreating, setIsCreating] = useState(false);
const [isTransitionEnded, setIsTransitionEnded] = useState(true); const [isTransitionEnded, setIsTransitionEnded] = useState(true);
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { prefillDeal, setPrefillDeal } = useDealPageContext(); const { prefillDeal, setPrefillDeal } = usePrefillDealContext();
return ( return (
<div <div
@@ -60,6 +60,7 @@ const CreateDealButton: FC<Props> = () => {
queryKey: ["getDealSummaries"], queryKey: ["getDealSummaries"],
}); });
setIsCreating(false); setIsCreating(false);
setPrefillDeal(undefined);
}); });
}} }}
/> />

View File

@@ -5,17 +5,18 @@ import { useForm } from "@mantine/form";
import styles from "./CreateDealForm.module.css"; import styles from "./CreateDealForm.module.css";
import ClientAutocomplete from "../../Selects/ClientAutocomplete/ClientAutocomplete.tsx"; import ClientAutocomplete from "../../Selects/ClientAutocomplete/ClientAutocomplete.tsx";
import { DateTimePicker } from "@mantine/dates"; 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 BaseMarketplaceSelect from "../../Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
import ServicePriceCategorySelect from "../../Selects/ServicePriceCategorySelect/ServicePriceCategorySelect.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 = { type Props = {
onSubmit: (quickDeal: QuickDeal) => void; onSubmit: (quickDeal: QuickDeal) => void;
onCancel: () => void; onCancel: () => void;
}; };
const CreateDealFrom: FC<Props> = ({ onSubmit, onCancel }) => { const CreateDealFrom: FC<Props> = ({ onSubmit, onCancel }) => {
const { prefillOnOpen, prefillDeal } = useDealPageContext(); const { prefillOnOpen, prefillDeal } = usePrefillDealContext();
const form = useForm<QuickDeal>({ const form = useForm<QuickDeal>({
initialValues: { initialValues: {
name: "", name: "",

View File

@@ -1,19 +1,9 @@
import { createContext, Dispatch, FC, SetStateAction, useContext, useState } from "react"; import { createContext, FC, useContext, useState } from "react";
import { DealSchema, DealService } from "../../../client"; import { DealSchema } from "../../../client";
import { useDisclosure } from "@mantine/hooks";
type DealPageContextState = { type DealPageContextState = {
selectedDeal?: DealSchema; selectedDeal?: DealSchema;
setSelectedDeal: (deal: DealSchema | undefined) => void; 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>( const DealPageContext = createContext<DealPageContextState | undefined>(
@@ -23,34 +13,10 @@ const useDealPageContextState = () => {
const [selectedDeal, setSelectedDeal] = useState<DealSchema | undefined>( const [selectedDeal, setSelectedDeal] = useState<DealSchema | undefined>(
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 { return {
selectedDeal, selectedDeal,
setSelectedDeal, setSelectedDeal,
prefillOpened,
prefillOnClose,
prefillOnOpen,
selectedPrefillDeal,
selectPrefillDeal,
prefillDeal,
setPrefillDeal,
}; };
}; };

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

View File

@@ -1,15 +1,15 @@
import { FC, useEffect } from "react"; import { FC, useEffect } from "react";
import { Button, Drawer, Flex, rem, TextInput } from "@mantine/core"; import { Button, Drawer, Flex, rem, TextInput } from "@mantine/core";
import { useDealPageContext } from "../../contexts/DealPageContext.tsx";
import DealsTable from "./components/tables/DealsTable/DealsTable.tsx"; import DealsTable from "./components/tables/DealsTable/DealsTable.tsx";
import Preview from "./components/Preview/Preview.tsx"; import Preview from "./components/Preview/Preview.tsx";
import styles from "./DealPrefillDrawer.module.css"; import styles from "./DealPrefillDrawer.module.css";
import BaseMarketplaceSelect from "../../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx"; import BaseMarketplaceSelect from "../../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
import usePrefillDeal from "./hooks/usePrefillDeal.tsx"; import usePrefillDeal from "./hooks/usePrefillDeal.tsx";
import { notifications } from "../../../../shared/lib/notifications.ts"; import { notifications } from "../../../../shared/lib/notifications.ts";
import { usePrefillDealContext } from "../../contexts/PrefillDealContext.tsx";
const DealPrefillDrawer: FC = () => { const DealPrefillDrawer: FC = () => {
const { prefillOpened, prefillOnClose, selectedPrefillDeal, setPrefillDeal, prefillDeal } = useDealPageContext(); const { prefillOpened, prefillOnClose, selectedPrefillDeal, setPrefillDeal, prefillDeal } = usePrefillDealContext();
const { data, form } = usePrefillDeal(); const { data, form } = usePrefillDeal();
useEffect(() => { useEffect(() => {

View File

@@ -1,7 +1,6 @@
.container { .container {
display: flex; display: flex;
gap: rem(10); gap: rem(10);
max-height: 95vh;
width: 50%; width: 50%;
} }
@@ -20,6 +19,7 @@
} }
.deal-container-wrapper { .deal-container-wrapper {
width: 100%;
border: dashed var(--item-border-size) var(--mantine-color-default-border); border: dashed var(--item-border-size) var(--mantine-color-default-border);
border-radius: var(--item-border-radius); border-radius: var(--item-border-radius);
padding: rem(10); padding: rem(10);

View File

@@ -1,12 +1,12 @@
import { FC } from "react"; import { FC } from "react";
import styles from "./Preview.module.css"; import styles from "./Preview.module.css";
import { ScrollArea, Skeleton, Title } from "@mantine/core"; import { ScrollArea, Skeleton, Title } from "@mantine/core";
import { useDealPageContext } from "../../../../contexts/DealPageContext.tsx";
import DealServicesTable from "../tables/DealServicesTable/DealServicesTable.tsx"; import DealServicesTable from "../tables/DealServicesTable/DealServicesTable.tsx";
import ProductPreview from "../ProductPreview/ProductPreview.tsx"; import ProductPreview from "../ProductPreview/ProductPreview.tsx";
import { usePrefillDealContext } from "../../../../contexts/PrefillDealContext.tsx";
const Preview: FC = () => { const Preview: FC = () => {
const { selectedPrefillDeal } = useDealPageContext(); const { selectedPrefillDeal } = usePrefillDealContext();
const getTotalPrice = () => { const getTotalPrice = () => {
if (!selectedPrefillDeal) return 0; if (!selectedPrefillDeal) return 0;
@@ -28,27 +28,29 @@ const Preview: FC = () => {
return ( return (
<div className={styles["container"]}> <div className={styles["container"]}>
<ScrollArea offsetScrollbars={"y"} w={"100%"}> <div className={styles["deal-container-wrapper"]}>
<Skeleton visible={!selectedPrefillDeal}> <ScrollArea offsetScrollbars={"y"} w={"100%"}>
<div className={styles["deal-container-wrapper"]}> <div style={{ height: "93vh" }}>
<Title order={4} mb={18}> <Skeleton visible={!selectedPrefillDeal}>
Общая стоимость всех услуг:{" "} <Title order={4} mb={18}>
{getTotalPrice().toLocaleString("ru")} Общая стоимость всех услуг:{" "}
</Title> {getTotalPrice().toLocaleString("ru")}
</Title>
<DealServicesTable items={selectedPrefillDeal?.services} /> <DealServicesTable items={selectedPrefillDeal?.services} />
<div className={styles["products-list"]}> <div className={styles["products-list"]}>
{selectedPrefillDeal?.products.map(product => ( {selectedPrefillDeal?.products.map(product => (
<ProductPreview <ProductPreview
key={product.product.id} key={product.product.id}
product={product} product={product}
/> />
))} ))}
</div> </div>
</Skeleton>
</div> </div>
</Skeleton> </ScrollArea>
</ScrollArea> </div>
</div> </div>
); );
}; };

View File

@@ -16,7 +16,6 @@ const DealServicesTable: FC<Props> = ({ items }) => {
<Flex <Flex
direction={"column"} direction={"column"}
gap={rem(10)} gap={rem(10)}
h={"100%"}
mb={10}> mb={10}>
<Flex <Flex
h={"100%"} h={"100%"}

View File

@@ -1,15 +1,15 @@
import { FC, useEffect } from "react"; import { FC, useEffect } from "react";
import useDealsTableColumns from "./columns.tsx"; import useDealsTableColumns from "./columns.tsx";
import { DealSummary } from "../../../../../../../client"; import { DealSummary } from "../../../../../../../client";
import { useDealPageContext } from "../../../../../contexts/DealPageContext.tsx";
import { BaseTable } from "../../../../../../../components/BaseTable/BaseTable.tsx"; import { BaseTable } from "../../../../../../../components/BaseTable/BaseTable.tsx";
import { usePrefillDealContext } from "../../../../../contexts/PrefillDealContext.tsx";
type Props = { type Props = {
items: DealSummary[]; items: DealSummary[];
}; };
const DealsTable: FC<Props> = ({ items }) => { const DealsTable: FC<Props> = ({ items }) => {
const { selectPrefillDeal } = useDealPageContext(); const { selectPrefillDeal } = usePrefillDealContext();
const columns = useDealsTableColumns(); const columns = useDealsTableColumns();
const defaultSorting = [{ id: "createdAt", desc: false }]; const defaultSorting = [{ id: "createdAt", desc: false }];
@@ -36,6 +36,9 @@ const DealsTable: FC<Props> = ({ items }) => {
mantinePaginationProps: { mantinePaginationProps: {
showRowsPerPage: false, showRowsPerPage: false,
}, },
enableStickyHeader: true,
enableStickyFooter: true,
mantineTableContainerProps: { style: { maxHeight: "81vh", height: "81vh" } },
} }
} }
/> />

View File

@@ -2,7 +2,7 @@ import { useMemo } from "react";
import { MRT_ColumnDef } from "mantine-react-table"; import { MRT_ColumnDef } from "mantine-react-table";
import { ActionIcon, Image, Radio } from "@mantine/core"; import { ActionIcon, Image, Radio } from "@mantine/core";
import { DealSummary } from "../../../../../../../client"; import { DealSummary } from "../../../../../../../client";
import { useDealPageContext } from "../../../../../contexts/DealPageContext.tsx"; import { usePrefillDealContext } from "../../../../../contexts/PrefillDealContext.tsx";
const useDealsTableColumns = () => { const useDealsTableColumns = () => {
return useMemo<MRT_ColumnDef<DealSummary>[]>( return useMemo<MRT_ColumnDef<DealSummary>[]>(
@@ -13,7 +13,7 @@ const useDealsTableColumns = () => {
size: 5, size: 5,
enableSorting: false, enableSorting: false,
Cell: ({ row }) => { Cell: ({ row }) => {
const { selectPrefillDeal, selectedPrefillDeal } = useDealPageContext(); const { selectPrefillDeal, selectedPrefillDeal } = usePrefillDealContext();
const checked = row.original.id === selectedPrefillDeal?.id; const checked = row.original.id === selectedPrefillDeal?.id;
return ( return (
<Radio <Radio

View File

@@ -21,6 +21,7 @@ import DealsTable from "../../DealsPage/components/DealsTable/DealsTable.tsx";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { dateWithoutTimezone } from "../../../shared/lib/date.ts"; import { dateWithoutTimezone } from "../../../shared/lib/date.ts";
import DealPrefillDrawer from "../drawers/DealPrefillDrawer/DealPrefillDrawer.tsx"; import DealPrefillDrawer from "../drawers/DealPrefillDrawer/DealPrefillDrawer.tsx";
import { PrefillDealContextProvider } from "../contexts/PrefillDealContext.tsx";
enum DisplayMode { enum DisplayMode {
BOARD, BOARD,
@@ -317,101 +318,103 @@ export const LeadsPage: FC = () => {
boxShadow: "none", boxShadow: "none",
}}> }}>
<DealPageContextProvider> <DealPageContextProvider>
<PageBlock style={{ flex: 0 }}> <PrefillDealContextProvider>
<Flex <PageBlock style={{ flex: 0 }}>
align={"center"}
justify={"space-between"}>
<Flex <Flex
gap={rem(10)} align={"center"}
direction={"column"} justify={"space-between"}>
align={"center"}> <Flex
<Text size={"xs"}>Вид</Text> gap={rem(10)}
<Flex gap={rem(10)}> direction={"column"}
<ActionIcon align={"center"}>
onClick={() => <Text size={"xs"}>Вид</Text>
setDisplayMode(DisplayMode.BOARD) <Flex gap={rem(10)}>
} <ActionIcon
variant={ onClick={() =>
displayMode === DisplayMode.BOARD setDisplayMode(DisplayMode.BOARD)
? "filled" }
: "default" variant={
}> displayMode === DisplayMode.BOARD
<IconMenuDeep ? "filled"
style={{ rotate: "-90deg" }} : "default"
/> }>
</ActionIcon> <IconMenuDeep
<ActionIcon style={{ rotate: "-90deg" }}
onClick={() => />
setDisplayMode(DisplayMode.TABLE) </ActionIcon>
} <ActionIcon
variant={ onClick={() =>
displayMode === DisplayMode.TABLE setDisplayMode(DisplayMode.TABLE)
? "filled" }
: "default" variant={
}> displayMode === DisplayMode.TABLE
<IconMenu2 /> ? "filled"
</ActionIcon> : "default"
}>
<IconMenu2 />
</ActionIcon>
</Flex>
</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> </Flex>
<motion.div </PageBlock>
key={displayMode} <PageBlock
initial={{ opacity: 0 }} style={{
animate={{ opacity: 1 }} display: "flex",
transition={{ duration: 0.2 }}> flexDirection: "column",
<div flex: 1,
className={styles["top-panel"]} height: "100%",
style={{ }}>
display: {getBody()}
displayMode === DisplayMode.TABLE </PageBlock>
? "flex" <DealEditDrawer />
: "none", <DealPrefillDrawer />
}}> </PrefillDealContextProvider>
<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 />
</DealPageContextProvider> </DealPageContextProvider>
</PageBlock> </PageBlock>
); );