crap
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import {FC} from "react";
|
||||
import {useDealServicesTableColumns} from "./columns.tsx";
|
||||
import {BaseTable} from "../../../../components/BaseTable/BaseTable.tsx";
|
||||
import {DealServiceSchema} from "../../../../client";
|
||||
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
|
||||
import {MRT_TableOptions} from "mantine-react-table";
|
||||
import {ActionIcon, Box, Button, Flex, rem, Tooltip} from "@mantine/core";
|
||||
import {openContextModal} from "@mantine/modals";
|
||||
import {IconTrash} from "@tabler/icons-react";
|
||||
|
||||
type RestProps = {
|
||||
onMultipleDelete?: (items: DealServiceSchema[]) => void;
|
||||
}
|
||||
type Props = CRUDTableProps<DealServiceSchema> & RestProps;
|
||||
const DealServicesTable: FC<Props> = (
|
||||
{
|
||||
items,
|
||||
onChange,
|
||||
onDelete,
|
||||
onCreate,
|
||||
onSelectionChange,
|
||||
onMultipleDelete,
|
||||
tableRef
|
||||
}) => {
|
||||
const onQuantityChange = (service: DealServiceSchema, quantity: number) => {
|
||||
if (!onChange) return;
|
||||
if (quantity <= 0 && onDelete) {
|
||||
onDelete(service);
|
||||
return;
|
||||
}
|
||||
onChange({...service, quantity});
|
||||
}
|
||||
const columns = useDealServicesTableColumns({
|
||||
onChange: onQuantityChange,
|
||||
data: items
|
||||
});
|
||||
const onCreateClick = () => {
|
||||
if (!onCreate) return;
|
||||
openContextModal({
|
||||
title: "Добавление услуги",
|
||||
modal: "addDealService",
|
||||
innerProps: {
|
||||
onCreate: (event) => onCreate(event as DealServiceSchema)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
return (
|
||||
<BaseTable
|
||||
ref={tableRef}
|
||||
data={items}
|
||||
columns={columns}
|
||||
onSelectionChange={onSelectionChange}
|
||||
restProps={{
|
||||
enableGrouping: true,
|
||||
initialState: {grouping: ["service.category"]},
|
||||
enableColumnActions: false,
|
||||
enableSorting: false,
|
||||
enableBottomToolbar: true,
|
||||
enableRowActions: true,
|
||||
enableRowSelection: true,
|
||||
renderBottomToolbar: ({table}) => (
|
||||
<Flex justify={"flex-end"} gap={rem(10)} p={rem(10)}>
|
||||
{(onMultipleDelete && table.getSelectedRowModel().rows.length > 0) && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
onMultipleDelete(table.getSelectedRowModel().rows.map(row => row.original))
|
||||
}}
|
||||
variant={"filled"}
|
||||
color={"red"}
|
||||
>
|
||||
Удалить выбранные
|
||||
</Button>
|
||||
)}
|
||||
<Button onClick={onCreateClick} variant={"default"}>
|
||||
Добавить услугу
|
||||
</Button>
|
||||
|
||||
</Flex>
|
||||
),
|
||||
renderRowActions: ({row}) => (
|
||||
<Flex gap="md">
|
||||
|
||||
<Tooltip label="Удалить">
|
||||
<ActionIcon onClick={() => {
|
||||
if (onDelete) onDelete(row.original);
|
||||
}} variant={"default"}>
|
||||
<IconTrash/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
)
|
||||
} as MRT_TableOptions<DealServiceSchema>}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default DealServicesTable;
|
||||
66
src/pages/LeadsPage/components/DealServicesTable/columns.tsx
Normal file
66
src/pages/LeadsPage/components/DealServicesTable/columns.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import {MRT_ColumnDef} from "mantine-react-table";
|
||||
import {useMemo} from "react";
|
||||
import {DealServiceSchema} from "../../../../client";
|
||||
import PlusMinusInput from "../../../../components/PlusMinusInput/PlusMinusInput.tsx";
|
||||
|
||||
type Props = {
|
||||
onChange: (service: DealServiceSchema, quantity: number) => void;
|
||||
data: DealServiceSchema[];
|
||||
}
|
||||
|
||||
export const useDealServicesTableColumns = (props: Props) => {
|
||||
const {onChange, data} = props;
|
||||
const totalPrice = useMemo(() =>
|
||||
data.reduce((acc, row) => acc + row.quantity * row.service.price, 0)
|
||||
,
|
||||
[data]);
|
||||
|
||||
return useMemo<MRT_ColumnDef<DealServiceSchema>[]>(() => [
|
||||
{
|
||||
accessorKey: "service.category",
|
||||
header: "Категория",
|
||||
accessorFn: (row) => row.service.category.name,
|
||||
},
|
||||
{
|
||||
enableGrouping: false,
|
||||
accessorKey: "service.name",
|
||||
header: "Услуга",
|
||||
},
|
||||
{
|
||||
enableGrouping: false,
|
||||
accessorKey: "service.price",
|
||||
header: "Цена",
|
||||
},
|
||||
{
|
||||
enableGrouping: false,
|
||||
accessorKey: "quantity",
|
||||
header: "Количество",
|
||||
Cell: ({row}) => {
|
||||
return (
|
||||
<PlusMinusInput
|
||||
value={row.original.quantity}
|
||||
onChange={(value) => onChange(row.original, value)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
enableGrouping: false,
|
||||
header: "Сумма",
|
||||
Cell: ({row}) => {
|
||||
return row.original.quantity * row.original.service.price;
|
||||
},
|
||||
aggregationFn: "sum",
|
||||
AggregatedCell: ({cell}) => {
|
||||
return <>Итоговая сумма по категории: {" "}
|
||||
{
|
||||
cell.row.subRows?.reduce((acc, row) =>
|
||||
acc + row.original.quantity * row.original.service.price, 0)
|
||||
}
|
||||
</>;
|
||||
},
|
||||
Footer: <>Итоговая сумма по услугам: {totalPrice}</>
|
||||
|
||||
}
|
||||
], [onChange]);
|
||||
}
|
||||
34
src/pages/LeadsPage/contexts/DealPageContext.tsx
Normal file
34
src/pages/LeadsPage/contexts/DealPageContext.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import {createContext, FC, useContext, useState} from "react";
|
||||
import {DealSchema} from "../../../client";
|
||||
|
||||
type DealPageContextState = {
|
||||
selectedDeal?: DealSchema;
|
||||
setSelectedDeal: (deal: DealSchema | undefined) => void;
|
||||
}
|
||||
|
||||
const DealPageContext = createContext<DealPageContextState | undefined>(undefined);
|
||||
const useDealPageContextState = () => {
|
||||
const [selectedDeal, setSelectedDeal] = useState<DealSchema | undefined>(undefined);
|
||||
return {selectedDeal, setSelectedDeal};
|
||||
}
|
||||
|
||||
type DealPageContextProviderProps = {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const DealPageContextProvider: FC<DealPageContextProviderProps> = ({children}) => {
|
||||
const state = useDealPageContextState();
|
||||
return (
|
||||
<DealPageContext.Provider value={state}>
|
||||
{children}
|
||||
</DealPageContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const useDealPageContext = () => {
|
||||
const context = useContext(DealPageContext);
|
||||
if (!context) {
|
||||
throw new Error('useDealPageContext must be used within a DealPageContextProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
175
src/pages/LeadsPage/drawers/DealEditDrawer/DealEditDrawer.tsx
Normal file
175
src/pages/LeadsPage/drawers/DealEditDrawer/DealEditDrawer.tsx
Normal file
@@ -0,0 +1,175 @@
|
||||
import {Drawer, Text} from "@mantine/core";
|
||||
import {FC, useRef} from "react";
|
||||
import DealServicesTable from "../../components/DealServicesTable/DealServicesTable.tsx";
|
||||
import {useDealPageContext} from "../../contexts/DealPageContext.tsx";
|
||||
import {DealService, DealServiceSchema} from "../../../../client";
|
||||
import {notifications} from "../../../../shared/lib/notifications.ts";
|
||||
import {modals} from "@mantine/modals";
|
||||
import {BaseTableRef} from "../../../../components/BaseTable/BaseTable.tsx";
|
||||
|
||||
const useDealServicesTableState = () => {
|
||||
const {selectedDeal, setSelectedDeal} = useDealPageContext();
|
||||
const tableRef = useRef<BaseTableRef<DealServiceSchema>>(null);
|
||||
|
||||
const onServiceUpdate = (service: DealServiceSchema) => {
|
||||
if (!selectedDeal) return;
|
||||
DealService.updateDealServiceQuantity({
|
||||
requestBody: {
|
||||
dealId: selectedDeal.id,
|
||||
serviceId: service.service.id,
|
||||
quantity: service.quantity
|
||||
}
|
||||
}).then(async ({ok, message}) => {
|
||||
|
||||
if (!ok) {
|
||||
notifications.guess(ok, {message});
|
||||
return;
|
||||
}
|
||||
await DealService.getDealById({dealId: selectedDeal.id})
|
||||
.then(setSelectedDeal)
|
||||
})
|
||||
}
|
||||
|
||||
const onServiceDelete = (service: DealServiceSchema) => {
|
||||
if (!selectedDeal) return;
|
||||
modals.openConfirmModal({
|
||||
title: "Удаление услуги",
|
||||
children: (
|
||||
<>
|
||||
<Text>
|
||||
Вы уверены, что хотите удалить услугу:
|
||||
</Text>
|
||||
<Text>
|
||||
{service.service.name}?
|
||||
</Text>
|
||||
|
||||
</>
|
||||
|
||||
),
|
||||
onConfirm: () => {
|
||||
DealService.deleteDealService({
|
||||
requestBody: {
|
||||
dealId: selectedDeal.id,
|
||||
serviceId: service.service.id
|
||||
}
|
||||
}).then(async ({ok, message}) => {
|
||||
if (!ok) {
|
||||
notifications.guess(ok, {message});
|
||||
return;
|
||||
}
|
||||
await DealService.getDealById({dealId: selectedDeal.id})
|
||||
.then(setSelectedDeal)
|
||||
})
|
||||
},
|
||||
labels: {
|
||||
cancel: "Отмена",
|
||||
confirm: "Удалить"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const onServiceCreate = (service: DealServiceSchema) => {
|
||||
console.log('-------Drawer')
|
||||
console.log(service);
|
||||
if (!selectedDeal) return;
|
||||
DealService.addDealService({
|
||||
requestBody: {
|
||||
dealId: selectedDeal.id,
|
||||
serviceId: service.service.id,
|
||||
quantity: service.quantity
|
||||
}
|
||||
}).then(async ({ok, message}) => {
|
||||
if (!ok) {
|
||||
notifications.guess(ok, {message});
|
||||
return;
|
||||
}
|
||||
await DealService.getDealById({dealId: selectedDeal.id})
|
||||
.then(setSelectedDeal)
|
||||
})
|
||||
}
|
||||
|
||||
const onsServiceMultipleDelete = (items: DealServiceSchema[]) => {
|
||||
if (!selectedDeal) return;
|
||||
modals.openConfirmModal({
|
||||
title: "Удаление услуг",
|
||||
children: (
|
||||
<>
|
||||
<Text>
|
||||
Вы уверены, что хотите удалить выбранные услуги?
|
||||
</Text>
|
||||
</>
|
||||
),
|
||||
onConfirm: () => {
|
||||
DealService.deleteMultipleDealServices({
|
||||
requestBody: {
|
||||
dealId: selectedDeal.id,
|
||||
serviceIds: items.map(item => item.service.id)
|
||||
}
|
||||
}).then(async ({ok, message}) => {
|
||||
if (!ok) {
|
||||
notifications.guess(ok, {message});
|
||||
return;
|
||||
}
|
||||
await DealService.getDealById({dealId: selectedDeal.id})
|
||||
.then(setSelectedDeal)
|
||||
})
|
||||
},
|
||||
labels: {
|
||||
cancel: "Отмена",
|
||||
confirm: "Удалить"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
onServiceUpdate,
|
||||
onServiceDelete,
|
||||
onServiceCreate,
|
||||
onsServiceMultipleDelete,
|
||||
tableRef,
|
||||
services: selectedDeal?.services || []
|
||||
}
|
||||
}
|
||||
|
||||
const DealEditDrawerServicesTable = () => {
|
||||
const {
|
||||
services,
|
||||
tableRef,
|
||||
onServiceCreate,
|
||||
onServiceUpdate,
|
||||
onServiceDelete,
|
||||
onsServiceMultipleDelete
|
||||
} = useDealServicesTableState();
|
||||
|
||||
return (<DealServicesTable
|
||||
tableRef={tableRef}
|
||||
items={services}
|
||||
onChange={onServiceUpdate}
|
||||
onDelete={onServiceDelete}
|
||||
onCreate={onServiceCreate}
|
||||
onMultipleDelete={onsServiceMultipleDelete}
|
||||
/>)
|
||||
}
|
||||
const useDealEditDrawerState = () => {
|
||||
const {selectedDeal, setSelectedDeal} = useDealPageContext();
|
||||
return {
|
||||
isVisible: selectedDeal !== undefined,
|
||||
onClose: () => setSelectedDeal(undefined)
|
||||
}
|
||||
}
|
||||
|
||||
const DealEditDrawer: FC = () => {
|
||||
const {isVisible, onClose} = useDealEditDrawerState();
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
size={"95%"}
|
||||
position={"right"}
|
||||
onClose={onClose}
|
||||
opened={isVisible}>
|
||||
<DealEditDrawerServicesTable/>
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
|
||||
export default DealEditDrawer;
|
||||
55
src/pages/LeadsPage/modals/AddDealServiceModal.tsx
Normal file
55
src/pages/LeadsPage/modals/AddDealServiceModal.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import {ContextModalProps} from "@mantine/modals";
|
||||
import BaseFormModal, {CreateEditFormProps} from "../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
|
||||
import {DealServiceSchema} from "../../../client";
|
||||
import {useForm} from "@mantine/form";
|
||||
import {NumberInput} from "@mantine/core";
|
||||
import ServiceSelect from "../../../components/ServiceSelect/ServiceSelect.tsx";
|
||||
|
||||
type Props = CreateEditFormProps<Partial<DealServiceSchema>>;
|
||||
const AddDealServiceModal = ({
|
||||
context,
|
||||
id,
|
||||
innerProps
|
||||
}: ContextModalProps<Props>) => {
|
||||
const form = useForm<Partial<DealServiceSchema>>({
|
||||
initialValues: {
|
||||
service: undefined,
|
||||
quantity: 0,
|
||||
},
|
||||
validate: {
|
||||
service: (service?: DealServiceSchema['service']) => service !== undefined ? null : "Необходимо выбрать услугу",
|
||||
quantity: (quantity?: number) => (quantity && quantity > 0) ? null : "Количество должно быть больше 0"
|
||||
}
|
||||
});
|
||||
const onClose = () => {
|
||||
context.closeContextModal(id);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<BaseFormModal
|
||||
{...innerProps}
|
||||
form={form}
|
||||
closeOnSubmit
|
||||
onClose={onClose}>
|
||||
<BaseFormModal.Body>
|
||||
<>
|
||||
<ServiceSelect
|
||||
placeholder={"Выберите услугу"}
|
||||
label={"Услуга"}
|
||||
{...form.getInputProps('service')}
|
||||
/>
|
||||
<NumberInput
|
||||
placeholder={"Введите количество"}
|
||||
label={"Количество"}
|
||||
min={1}
|
||||
{...form.getInputProps('quantity')}
|
||||
/>
|
||||
</>
|
||||
|
||||
</BaseFormModal.Body>
|
||||
</BaseFormModal>
|
||||
)
|
||||
}
|
||||
|
||||
export default AddDealServiceModal;
|
||||
@@ -5,6 +5,8 @@ import {DragDropContext} from "@hello-pangea/dnd";
|
||||
import {useDealSummaries} from "../hooks/useDealSummaries.tsx";
|
||||
import {DealStatus} from "../../../shared/enums/DealStatus.ts";
|
||||
import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
|
||||
import DealEditDrawer from "../drawers/DealEditDrawer/DealEditDrawer.tsx";
|
||||
import {DealPageContextProvider} from "../contexts/DealPageContext.tsx";
|
||||
|
||||
|
||||
export const LeadsPage: FC = () => {
|
||||
@@ -25,45 +27,50 @@ export const LeadsPage: FC = () => {
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<PageBlock>
|
||||
<div className={styles['container']}>
|
||||
<div className={styles['boards']}>
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
<Board
|
||||
withCreateButton
|
||||
summaries={summaries
|
||||
.filter(summary => summary.status == DealStatus.AWAITING_ACCEPTANCE)}
|
||||
title={"Ожидает приемки"}
|
||||
droppableId={"AWAITING_ACCEPTANCE"}
|
||||
/>
|
||||
<Board
|
||||
summaries={summaries
|
||||
.filter(summary => summary.status == DealStatus.PACKAGING)}
|
||||
title={"Упаковка"}
|
||||
droppableId={"PACKAGING"}
|
||||
/>
|
||||
<Board
|
||||
summaries={summaries
|
||||
.filter(summary => summary.status == DealStatus.AWAITING_SHIPMENT)}
|
||||
title={"Ожидает отгрузки"}
|
||||
droppableId={"AWAITING_SHIPMENT"}
|
||||
/>
|
||||
<Board
|
||||
summaries={summaries
|
||||
.filter(summary => summary.status == DealStatus.AWAITING_PAYMENT)}
|
||||
title={"Ожидает оплаты"}
|
||||
droppableId={"AWAITING_PAYMENT"}
|
||||
/>
|
||||
<Board
|
||||
summaries={summaries
|
||||
.filter(summary => summary.status == DealStatus.COMPLETED)}
|
||||
title={"Завершена"}
|
||||
droppableId={"COMPLETED"}
|
||||
/>
|
||||
</DragDropContext>
|
||||
<DealPageContextProvider>
|
||||
|
||||
<PageBlock>
|
||||
<div className={styles['container']}>
|
||||
<div className={styles['boards']}>
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
<Board
|
||||
withCreateButton
|
||||
summaries={summaries
|
||||
.filter(summary => summary.status == DealStatus.AWAITING_ACCEPTANCE)}
|
||||
title={"Ожидает приемки"}
|
||||
droppableId={"AWAITING_ACCEPTANCE"}
|
||||
/>
|
||||
<Board
|
||||
summaries={summaries
|
||||
.filter(summary => summary.status == DealStatus.PACKAGING)}
|
||||
title={"Упаковка"}
|
||||
droppableId={"PACKAGING"}
|
||||
/>
|
||||
<Board
|
||||
summaries={summaries
|
||||
.filter(summary => summary.status == DealStatus.AWAITING_SHIPMENT)}
|
||||
title={"Ожидает отгрузки"}
|
||||
droppableId={"AWAITING_SHIPMENT"}
|
||||
/>
|
||||
<Board
|
||||
summaries={summaries
|
||||
.filter(summary => summary.status == DealStatus.AWAITING_PAYMENT)}
|
||||
title={"Ожидает оплаты"}
|
||||
droppableId={"AWAITING_PAYMENT"}
|
||||
/>
|
||||
<Board
|
||||
summaries={summaries
|
||||
.filter(summary => summary.status == DealStatus.COMPLETED)}
|
||||
title={"Завершена"}
|
||||
droppableId={"COMPLETED"}
|
||||
/>
|
||||
</DragDropContext>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PageBlock>
|
||||
</PageBlock>
|
||||
<DealEditDrawer
|
||||
/>
|
||||
</DealPageContextProvider>
|
||||
|
||||
</>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user