feat: price by category
This commit is contained in:
@@ -1,22 +1,22 @@
|
||||
import {Box, Drawer, rem, Tabs, Text} from "@mantine/core";
|
||||
import {FC, useEffect, useRef} from "react";
|
||||
import { Box, Drawer, rem, Tabs, Text } from "@mantine/core";
|
||||
import { FC, useEffect, useRef } from "react";
|
||||
import DealServicesTable from "../../components/DealServicesTable/DealServicesTable.tsx";
|
||||
import {useDealPageContext} from "../../contexts/DealPageContext.tsx";
|
||||
import {DealProductSchema, DealService, DealServiceSchema} from "../../../../client";
|
||||
import {notifications} from "../../../../shared/lib/notifications.ts";
|
||||
import {modals} from "@mantine/modals";
|
||||
import {BaseTableRef} from "../../../../components/BaseTable/BaseTable.tsx";
|
||||
import { useDealPageContext } from "../../contexts/DealPageContext.tsx";
|
||||
import { DealProductSchema, DealService, DealServiceSchema } from "../../../../client";
|
||||
import { notifications } from "../../../../shared/lib/notifications.ts";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { BaseTableRef } from "../../../../components/BaseTable/BaseTable.tsx";
|
||||
import DealProductsTable from "../../components/DealProductsTable/DealProductsTable.tsx";
|
||||
import {IconBox, IconCalendarUser, IconSettings} from "@tabler/icons-react";
|
||||
import { IconBox, IconCalendarUser, IconSettings } from "@tabler/icons-react";
|
||||
import DealStatusChangeTable from "../../components/DealStatusChangeTable/DealStatusChangeTable.tsx";
|
||||
import DealEditDrawerGeneralTab from "./tabs/DealEditDrawerGeneralTab.tsx";
|
||||
import {useQueryClient} from "@tanstack/react-query";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import ProductAndServiceTab from "../../tabs/ProductAndServiceTab/ProductAndServiceTab.tsx";
|
||||
import {motion} from "framer-motion";
|
||||
import { motion } from "framer-motion";
|
||||
// import styles from './DealEditDrawer.module.css';
|
||||
|
||||
const useDealServicesTableState = () => {
|
||||
const {selectedDeal, setSelectedDeal} = useDealPageContext();
|
||||
const { selectedDeal, setSelectedDeal } = useDealPageContext();
|
||||
const tableRef = useRef<BaseTableRef<DealServiceSchema>>(null);
|
||||
|
||||
const onServiceUpdate = (service: DealServiceSchema) => {
|
||||
@@ -24,18 +24,18 @@ const useDealServicesTableState = () => {
|
||||
DealService.updateDealService({
|
||||
requestBody: {
|
||||
dealId: selectedDeal.id,
|
||||
service
|
||||
}
|
||||
}).then(async ({ok, message}) => {
|
||||
service,
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
|
||||
if (!ok) {
|
||||
notifications.guess(ok, {message});
|
||||
notifications.guess(ok, { message });
|
||||
return;
|
||||
}
|
||||
await DealService.getDealById({dealId: selectedDeal.id})
|
||||
.then(setSelectedDeal)
|
||||
})
|
||||
}
|
||||
await DealService.getDealById({ dealId: selectedDeal.id })
|
||||
.then(setSelectedDeal);
|
||||
});
|
||||
};
|
||||
const onServiceDelete = (service: DealServiceSchema) => {
|
||||
if (!selectedDeal) return;
|
||||
modals.openConfirmModal({
|
||||
@@ -56,23 +56,23 @@ const useDealServicesTableState = () => {
|
||||
DealService.deleteDealService({
|
||||
requestBody: {
|
||||
dealId: selectedDeal.id,
|
||||
serviceId: service.service.id
|
||||
}
|
||||
}).then(async ({ok, message}) => {
|
||||
serviceId: service.service.id,
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
if (!ok) {
|
||||
notifications.guess(ok, {message});
|
||||
notifications.guess(ok, { message });
|
||||
return;
|
||||
}
|
||||
await DealService.getDealById({dealId: selectedDeal.id})
|
||||
.then(setSelectedDeal)
|
||||
})
|
||||
await DealService.getDealById({ dealId: selectedDeal.id })
|
||||
.then(setSelectedDeal);
|
||||
});
|
||||
},
|
||||
labels: {
|
||||
cancel: "Отмена",
|
||||
confirm: "Удалить"
|
||||
}
|
||||
})
|
||||
}
|
||||
confirm: "Удалить",
|
||||
},
|
||||
});
|
||||
};
|
||||
const onServiceCreate = (service: DealServiceSchema) => {
|
||||
if (!selectedDeal) return;
|
||||
DealService.addDealService({
|
||||
@@ -80,17 +80,17 @@ const useDealServicesTableState = () => {
|
||||
dealId: selectedDeal.id,
|
||||
serviceId: service.service.id,
|
||||
quantity: service.quantity,
|
||||
price: service.price
|
||||
}
|
||||
}).then(async ({ok, message}) => {
|
||||
price: service.price,
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
if (!ok) {
|
||||
notifications.guess(ok, {message});
|
||||
notifications.guess(ok, { message });
|
||||
return;
|
||||
}
|
||||
await DealService.getDealById({dealId: selectedDeal.id})
|
||||
.then(setSelectedDeal)
|
||||
})
|
||||
}
|
||||
await DealService.getDealById({ dealId: selectedDeal.id })
|
||||
.then(setSelectedDeal);
|
||||
});
|
||||
};
|
||||
const onsServiceMultipleDelete = (items: DealServiceSchema[]) => {
|
||||
if (!selectedDeal) return;
|
||||
modals.openConfirmModal({
|
||||
@@ -106,23 +106,23 @@ const useDealServicesTableState = () => {
|
||||
DealService.deleteMultipleDealServices({
|
||||
requestBody: {
|
||||
dealId: selectedDeal.id,
|
||||
serviceIds: items.map(item => item.service.id)
|
||||
}
|
||||
}).then(async ({ok, message}) => {
|
||||
serviceIds: items.map(item => item.service.id),
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
if (!ok) {
|
||||
notifications.guess(ok, {message});
|
||||
notifications.guess(ok, { message });
|
||||
return;
|
||||
}
|
||||
await DealService.getDealById({dealId: selectedDeal.id})
|
||||
.then(setSelectedDeal)
|
||||
})
|
||||
await DealService.getDealById({ dealId: selectedDeal.id })
|
||||
.then(setSelectedDeal);
|
||||
});
|
||||
},
|
||||
labels: {
|
||||
cancel: "Отмена",
|
||||
confirm: "Удалить"
|
||||
}
|
||||
})
|
||||
}
|
||||
confirm: "Удалить",
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
onServiceUpdate,
|
||||
@@ -130,9 +130,9 @@ const useDealServicesTableState = () => {
|
||||
onServiceCreate,
|
||||
onsServiceMultipleDelete,
|
||||
tableRef,
|
||||
services: selectedDeal?.services || []
|
||||
}
|
||||
}
|
||||
services: selectedDeal?.services || [],
|
||||
};
|
||||
};
|
||||
const DealEditDrawerServicesTable = () => {
|
||||
const {
|
||||
services,
|
||||
@@ -140,7 +140,7 @@ const DealEditDrawerServicesTable = () => {
|
||||
onServiceCreate,
|
||||
onServiceUpdate,
|
||||
onServiceDelete,
|
||||
onsServiceMultipleDelete
|
||||
onsServiceMultipleDelete,
|
||||
} = useDealServicesTableState();
|
||||
|
||||
return (<DealServicesTable
|
||||
@@ -150,26 +150,26 @@ const DealEditDrawerServicesTable = () => {
|
||||
onDelete={onServiceDelete}
|
||||
onCreate={onServiceCreate}
|
||||
onMultipleDelete={onsServiceMultipleDelete}
|
||||
/>)
|
||||
}
|
||||
/>);
|
||||
};
|
||||
|
||||
const useDealProductTableState = () => {
|
||||
const {selectedDeal, setSelectedDeal} = useDealPageContext();
|
||||
const { selectedDeal, setSelectedDeal } = useDealPageContext();
|
||||
|
||||
const onProductUpdate = (product: DealProductSchema) => {
|
||||
if (!selectedDeal) return;
|
||||
DealService.updateDealProduct({
|
||||
requestBody: {
|
||||
dealId: selectedDeal.id,
|
||||
product: product
|
||||
}
|
||||
}).then(async ({ok, message}) => {
|
||||
notifications.guess(ok, {message});
|
||||
product: product,
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
if (!ok) return;
|
||||
await DealService.getDealById({dealId: selectedDeal.id})
|
||||
.then(setSelectedDeal)
|
||||
})
|
||||
}
|
||||
await DealService.getDealById({ dealId: selectedDeal.id })
|
||||
.then(setSelectedDeal);
|
||||
});
|
||||
};
|
||||
const onProductDelete = (product: DealProductSchema) => {
|
||||
if (!selectedDeal) return;
|
||||
modals.openConfirmModal({
|
||||
@@ -190,39 +190,39 @@ const useDealProductTableState = () => {
|
||||
DealService.deleteDealProduct({
|
||||
requestBody: {
|
||||
dealId: selectedDeal.id,
|
||||
productId: product.product.id
|
||||
}
|
||||
}).then(async ({ok, message}) => {
|
||||
productId: product.product.id,
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
if (!ok) {
|
||||
notifications.guess(ok, {message});
|
||||
notifications.guess(ok, { message });
|
||||
return;
|
||||
}
|
||||
await DealService.getDealById({dealId: selectedDeal.id})
|
||||
.then(setSelectedDeal)
|
||||
})
|
||||
await DealService.getDealById({ dealId: selectedDeal.id })
|
||||
.then(setSelectedDeal);
|
||||
});
|
||||
},
|
||||
labels: {
|
||||
cancel: "Отмена",
|
||||
confirm: "Удалить"
|
||||
}
|
||||
})
|
||||
}
|
||||
confirm: "Удалить",
|
||||
},
|
||||
});
|
||||
};
|
||||
const onProductCreate = (product: DealProductSchema) => {
|
||||
if (!selectedDeal) return;
|
||||
DealService.addDealProduct({
|
||||
requestBody: {
|
||||
dealId: selectedDeal.id,
|
||||
product: product
|
||||
}
|
||||
}).then(async ({ok, message}) => {
|
||||
product: product,
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
if (!ok) {
|
||||
notifications.guess(ok, {message});
|
||||
notifications.guess(ok, { message });
|
||||
return;
|
||||
}
|
||||
await DealService.getDealById({dealId: selectedDeal.id})
|
||||
.then(setSelectedDeal)
|
||||
})
|
||||
}
|
||||
await DealService.getDealById({ dealId: selectedDeal.id })
|
||||
.then(setSelectedDeal);
|
||||
});
|
||||
};
|
||||
const onProductMultipleDelete = (items: DealProductSchema[]) => {
|
||||
if (!selectedDeal) return;
|
||||
modals.openConfirmModal({
|
||||
@@ -238,32 +238,32 @@ const useDealProductTableState = () => {
|
||||
DealService.deleteMultipleDealProducts({
|
||||
requestBody: {
|
||||
dealId: selectedDeal.id,
|
||||
productIds: items.map(item => item.product.id)
|
||||
}
|
||||
}).then(async ({ok, message}) => {
|
||||
productIds: items.map(item => item.product.id),
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
if (!ok) {
|
||||
notifications.guess(ok, {message});
|
||||
notifications.guess(ok, { message });
|
||||
return;
|
||||
}
|
||||
await DealService.getDealById({dealId: selectedDeal.id})
|
||||
.then(setSelectedDeal)
|
||||
})
|
||||
await DealService.getDealById({ dealId: selectedDeal.id })
|
||||
.then(setSelectedDeal);
|
||||
});
|
||||
},
|
||||
labels: {
|
||||
cancel: "Отмена",
|
||||
confirm: "Удалить"
|
||||
}
|
||||
})
|
||||
}
|
||||
confirm: "Удалить",
|
||||
},
|
||||
});
|
||||
};
|
||||
return {
|
||||
clientId: selectedDeal?.clientId || -1,
|
||||
products: selectedDeal?.products || [],
|
||||
onProductUpdate,
|
||||
onProductDelete,
|
||||
onProductCreate,
|
||||
onProductMultipleDelete
|
||||
}
|
||||
}
|
||||
onProductMultipleDelete,
|
||||
};
|
||||
};
|
||||
const DealEditDrawerProductsTable = () => {
|
||||
const {
|
||||
products,
|
||||
@@ -283,79 +283,86 @@ const DealEditDrawerProductsTable = () => {
|
||||
onCreate={onProductCreate}
|
||||
|
||||
/>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const useDealStatusChangeState = () => {
|
||||
const {selectedDeal} = useDealPageContext();
|
||||
const { selectedDeal } = useDealPageContext();
|
||||
return {
|
||||
statusHistory: selectedDeal?.statusHistory || []
|
||||
}
|
||||
}
|
||||
statusHistory: selectedDeal?.statusHistory || [],
|
||||
};
|
||||
};
|
||||
const DealEditDrawerStatusChangeTable = () => {
|
||||
const {statusHistory} = useDealStatusChangeState();
|
||||
const { statusHistory } = useDealStatusChangeState();
|
||||
|
||||
return (
|
||||
<DealStatusChangeTable
|
||||
items={statusHistory}
|
||||
/>)
|
||||
}
|
||||
/>);
|
||||
};
|
||||
|
||||
const useDealEditDrawerState = () => {
|
||||
const {selectedDeal, setSelectedDeal} = useDealPageContext();
|
||||
const { selectedDeal, setSelectedDeal } = useDealPageContext();
|
||||
return {
|
||||
isVisible: selectedDeal !== undefined,
|
||||
onClose: () => setSelectedDeal(undefined)
|
||||
}
|
||||
}
|
||||
onClose: () => setSelectedDeal(undefined),
|
||||
};
|
||||
};
|
||||
|
||||
const DealEditDrawer: FC = () => {
|
||||
const {isVisible, onClose} = useDealEditDrawerState();
|
||||
const { isVisible, onClose } = useDealEditDrawerState();
|
||||
const queryClient = useQueryClient();
|
||||
useEffect(() => {
|
||||
if (isVisible) return;
|
||||
queryClient.invalidateQueries({queryKey: ["getDealSummaries"]})
|
||||
queryClient.invalidateQueries({ queryKey: ["getDealSummaries"] });
|
||||
}, [isVisible]);
|
||||
return (
|
||||
<Drawer
|
||||
size={"calc(100vw - 150px)"}
|
||||
position={"right"}
|
||||
onClose={onClose}
|
||||
removeScrollProps={{allowPinchZoom: true}}
|
||||
removeScrollProps={{ allowPinchZoom: true }}
|
||||
withCloseButton={false}
|
||||
opened={isVisible}
|
||||
styles={{body: {height: '100%'}}}
|
||||
styles={{
|
||||
body: {
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column", gap: rem(10),
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Tabs
|
||||
defaultValue={"general"}
|
||||
h={'100%'}
|
||||
flex={1}
|
||||
variant={"outline"}
|
||||
orientation={"vertical"}
|
||||
keepMounted={false}
|
||||
|
||||
>
|
||||
|
||||
<Tabs.List
|
||||
>
|
||||
<Tabs.Tab value={"general"} leftSection={<IconSettings/>}>
|
||||
<Tabs.Tab value={"general"} leftSection={<IconSettings />}>
|
||||
Общее
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab value={"history"} leftSection={<IconCalendarUser/>}>
|
||||
<Tabs.Tab value={"history"} leftSection={<IconCalendarUser />}>
|
||||
История
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab value={"servicesAndProducts"} leftSection={<IconBox/>}>
|
||||
<Tabs.Tab value={"servicesAndProducts"} leftSection={<IconBox />}>
|
||||
Товары и услуги
|
||||
</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel value={"general"}>
|
||||
<motion.div
|
||||
|
||||
initial={{opacity: 0}}
|
||||
animate={{opacity: 1}}
|
||||
transition={{duration: 0.2}}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
|
||||
<Box h={"100%"} w={"100%"} p={rem(10)}>
|
||||
<DealEditDrawerGeneralTab/>
|
||||
<DealEditDrawerGeneralTab />
|
||||
|
||||
</Box>
|
||||
</motion.div>
|
||||
@@ -364,40 +371,40 @@ const DealEditDrawer: FC = () => {
|
||||
<Tabs.Panel value={"history"}>
|
||||
<motion.div
|
||||
|
||||
initial={{opacity: 0}}
|
||||
animate={{opacity: 1}}
|
||||
transition={{duration: 0.2}}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
<Box p={rem(10)}>
|
||||
<DealEditDrawerStatusChangeTable/>
|
||||
<DealEditDrawerStatusChangeTable />
|
||||
</Box>
|
||||
</motion.div>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value={"servicesAndProducts"}>
|
||||
<motion.div
|
||||
|
||||
initial={{opacity: 0}}
|
||||
animate={{opacity: 1}}
|
||||
transition={{duration: 0.2}}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
<Box p={rem(10)}>
|
||||
<ProductAndServiceTab/>
|
||||
<ProductAndServiceTab />
|
||||
</Box>
|
||||
</motion.div>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value={"services"}>
|
||||
<Box p={rem(10)}>
|
||||
<DealEditDrawerServicesTable/>
|
||||
<DealEditDrawerServicesTable />
|
||||
</Box>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value={"products"}>
|
||||
<Box p={rem(10)}>
|
||||
<DealEditDrawerProductsTable/>
|
||||
<DealEditDrawerProductsTable />
|
||||
</Box>
|
||||
</Tabs.Panel>
|
||||
</Tabs>
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default DealEditDrawer;
|
||||
@@ -103,7 +103,7 @@ const Content: FC<Props> = ({ deal }) => {
|
||||
};
|
||||
return (
|
||||
<form onSubmit={form.onSubmit((values) => handleSubmit(values))}>
|
||||
<Flex direction={"column"}>
|
||||
<Flex direction={"column"} justify={"space-between"} h={"100%"}>
|
||||
<Fieldset legend={`Общие параметры [ID: ${deal.id}]`}>
|
||||
<Flex direction={"column"} gap={rem(10)}>
|
||||
<TextInput
|
||||
@@ -122,6 +122,15 @@ const Content: FC<Props> = ({ deal }) => {
|
||||
placeholder={"Текущий статус"}
|
||||
label={"Текущий статус"}
|
||||
value={DealStatusDictionary[deal.currentStatus as DealStatus]} />
|
||||
{deal.category && (
|
||||
<TextInput
|
||||
disabled
|
||||
placeholder={"Категория"}
|
||||
label={"Категория"}
|
||||
value={deal.category.name}
|
||||
|
||||
/>
|
||||
)}
|
||||
<Textarea
|
||||
label={"Коментарий к сделке"}
|
||||
placeholder={"Введите коментарий к сделке"}
|
||||
@@ -141,34 +150,6 @@ const Content: FC<Props> = ({ deal }) => {
|
||||
/>
|
||||
</Flex>
|
||||
</Fieldset>
|
||||
<Fieldset legend={"Клиент"}>
|
||||
<TextInput
|
||||
disabled
|
||||
placeholder={"Название"}
|
||||
label={"Название"}
|
||||
value={deal.client.name}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder={"Введите телефон"}
|
||||
label={"Телефон клиента"}
|
||||
{...form.getInputProps("client.details.phoneNumber")}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder={"Введите email"}
|
||||
label={"Email"}
|
||||
{...form.getInputProps("client.details.email")}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder={"Введите телеграм"}
|
||||
label={"Телеграм"}
|
||||
{...form.getInputProps("client.details.telegram")}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder={"Введите ИНН"}
|
||||
label={"ИНН"}
|
||||
{...form.getInputProps("client.details.inn")}
|
||||
/>
|
||||
</Fieldset>
|
||||
<Flex mt={"md"} gap={rem(10)} align={"center"} justify={"flex-end"}>
|
||||
<Flex align={"center"} gap={rem(10)} justify={"center"}>
|
||||
|
||||
@@ -199,10 +180,7 @@ const Content: FC<Props> = ({ deal }) => {
|
||||
:
|
||||
<ButtonCopyControlled
|
||||
onCopyClick={() => {
|
||||
// get current datetime for filename, replaced dots with _
|
||||
const date = getCurrentDateTimeForFilename();
|
||||
|
||||
|
||||
FileSaver.saveAs(`${import.meta.env.VITE_API_URL}/deal/document/${deal.id}`,
|
||||
`bill_${deal.id}_${date}.pdf`);
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user