This commit is contained in:
2024-04-24 01:21:02 +03:00
parent 862f3fa3c1
commit c4e106576e
13 changed files with 205 additions and 86 deletions

View File

@@ -8,7 +8,7 @@
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"generate-client": "openapi --input http://test.crm.denco.store/api/openapi.json --output ./src/client --client axios --useOptions --useUnionTypes"
"generate-client": "openapi --input http://127.0.0.1:8000/openapi.json --output ./src/client --client axios --useOptions --useUnionTypes"
},
"dependencies": {
"@hello-pangea/dnd": "^16.5.0",

View File

@@ -45,6 +45,8 @@ export type { DealSchema } from './models/DealSchema';
export type { DealServiceSchema } from './models/DealServiceSchema';
export type { DealStatusHistorySchema } from './models/DealStatusHistorySchema';
export type { DealSummary } from './models/DealSummary';
export type { DealSummaryReorderRequest } from './models/DealSummaryReorderRequest';
export type { DealSummaryReorderResponse } from './models/DealSummaryReorderResponse';
export type { DealSummaryResponse } from './models/DealSummaryResponse';
export type { DealUpdateGeneralInfoRequest } from './models/DealUpdateGeneralInfoRequest';
export type { DealUpdateGeneralInfoResponse } from './models/DealUpdateGeneralInfoResponse';

View File

@@ -5,7 +5,7 @@
export type ClientDetailsSchema = {
address?: (string | null);
phoneNumber?: (string | null);
inn?: (number | null);
inn?: (string | null);
email?: (string | null);
};

View File

@@ -6,5 +6,6 @@ export type DealGeneralInfoSchema = {
name: string;
isDeleted: boolean;
isCompleted: boolean;
comment: string;
};

View File

@@ -18,5 +18,6 @@ export type DealSchema = {
isDeleted: boolean;
isCompleted: boolean;
client: ClientSchema;
comment: string;
};

View File

@@ -9,5 +9,6 @@ export type DealSummary = {
changedAt: string;
status: number;
totalPrice: number;
rank: number;
};

View File

@@ -0,0 +1,12 @@
/* generated using openapi-typescript-codegen -- do no edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type DealSummaryReorderRequest = {
dealId: number;
newStatus: number;
rank: number;
nextStatusDeadline: string;
comment: string;
};

View File

@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do no edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type DealSummaryReorderResponse = {
ok: boolean;
message: string;
};

View File

@@ -23,6 +23,8 @@ import type { DealGetAllResponse } from '../models/DealGetAllResponse';
import type { DealQuickCreateRequest } from '../models/DealQuickCreateRequest';
import type { DealQuickCreateResponse } from '../models/DealQuickCreateResponse';
import type { DealSchema } from '../models/DealSchema';
import type { DealSummaryReorderRequest } from '../models/DealSummaryReorderRequest';
import type { DealSummaryReorderResponse } from '../models/DealSummaryReorderResponse';
import type { DealSummaryResponse } from '../models/DealSummaryResponse';
import type { DealUpdateGeneralInfoRequest } from '../models/DealUpdateGeneralInfoRequest';
import type { DealUpdateGeneralInfoResponse } from '../models/DealUpdateGeneralInfoResponse';
@@ -105,6 +107,26 @@ export class DealService {
url: '/deal/summaries',
});
}
/**
* Reorder
* @returns DealSummaryReorderResponse Successful Response
* @throws ApiError
*/
public static reorderDealSummaries({
requestBody,
}: {
requestBody: DealSummaryReorderRequest,
}): CancelablePromise<DealSummaryReorderResponse> {
return __request(OpenAPI, {
method: 'POST',
url: '/deal/summaries/reorder',
body: requestBody,
mediaType: 'application/json',
errors: {
422: `Validation Error`,
},
});
}
/**
* Get All
* @returns DealGetAllResponse Successful Response

View File

@@ -46,7 +46,7 @@ export const Board: FC<Props> = ({droppableId, title, summaries, withCreateButto
(
<Draggable
draggableId={summary.id.toString()}
index={1}
index={summary.rank}
key={summary.id}
>
{(provided) => (

View File

@@ -1,19 +1,69 @@
import {ContextModalProps} from "@mantine/modals";
import {Button, Text} from "@mantine/core";
import {Button, Flex, rem, Textarea} from "@mantine/core";
import {DateTimePicker, DateValue} from "@mantine/dates";
import {useForm} from "@mantine/form";
import {DealSummaryReorderRequest} from "../../client";
type Deadline = {
datetime: DateValue,
comment: string
}
type Props = {
request:DealSummaryReorderRequest,
onSubmit: (
deadline: Deadline,
request: DealSummaryReorderRequest,
) => void;
}
const EnterDeadlineModal = ({
context,
id,
innerProps,
}: ContextModalProps<{ modalBody: string }>) => {
}: ContextModalProps<Props>) => {
const form = useForm<Deadline>({
initialValues: {
datetime: null,
comment: '',
},
validate: {
datetime: (datetime) => datetime !== null ? null : 'Необходимо ввести дедлайн',
}
})
const onCancelClick = () => {
context.closeModal(id);
}
const onSubmit = (values: Deadline) => {
innerProps.onSubmit(values, innerProps.request);
}
return (
<>
<Text size="sm">{innerProps.modalBody}</Text>
<Button fullWidth mt="md" onClick={() => context.closeModal(id)}>
Close modal
</Button>
</>
<form onSubmit={form.onSubmit((values) => onSubmit(values))}>
<Flex direction={'column'} gap={10}>
<Flex direction={'column'} gap={rem(10)}>
<DateTimePicker
required
label={'Дата и время'}
placeholder={'Введите дату и время'}
{...form.getInputProps('date')}
/>
<Textarea
label={'Коментарий'}
placeholder={'Введите коментарий'}
{...form.getInputProps('comment')}
/>
</Flex>
<Flex justify={'flex-end'} gap={rem(10)}>
<Button
variant={'default'}
onClick={onCancelClick}
>Отменить</Button>
<Button>Сохранить</Button>
</Flex>
</Flex>
</form>
)
};
export default EnterDeadlineModal;

View File

@@ -1,6 +1,6 @@
import {FC} from "react";
import {useDealPageContext} from "../../../contexts/DealPageContext.tsx";
import {Button, Checkbox, Fieldset, Flex, Group, rem, TextInput} from "@mantine/core";
import {Button, Checkbox, Divider, Fieldset, Flex, Group, rem, Textarea, TextInput} from "@mantine/core";
import {useForm} from "@mantine/form";
import {ClientService, DealSchema, DealService} from "../../../../../client";
import {DealStatus, DealStatusDictionary} from "../../../../../shared/enums/DealStatus.ts";
@@ -63,77 +63,93 @@ const Content: FC<Props> = ({deal}) => {
}
return (
<form onSubmit={form.onSubmit((values) => handleSubmit(values))}>
<Fieldset legend={"Общие параметры"}>
<Flex direction={"column"} gap={rem(10)}>
<TextInput
placeholder={"Название сделки"}
label={"Название сделки"}
{...form.getInputProps('name')}
/>
<Flex direction={'column'}>
<Fieldset legend={"Общие параметры"}>
<Flex direction={"column"} gap={rem(10)}>
<TextInput
placeholder={"Название сделки"}
label={"Название сделки"}
{...form.getInputProps('name')}
/>
<TextInput
disabled
placeholder={"Дата создания"}
label={"Дата создания"}
value={new Date(deal.createdAt).toLocaleString('ru-RU')}
/>
<TextInput
disabled
placeholder={"Текущий статус"}
label={"Текущий статус"}
value={DealStatusDictionary[deal.currentStatus as DealStatus]}/>
<Textarea
label={'Коментарий к сделке'}
placeholder={'Введите коментарий к сделке'}
{...form.getInputProps('comment')}
/>
</Flex>
</Fieldset>
<Fieldset legend={"Клиент"}>
<TextInput
disabled
placeholder={"Дата создания"}
label={"Дата создания"}
value={new Date(deal.createdAt).toLocaleString('ru-RU')}
placeholder={"Название"}
label={"Название"}
value={deal.client.name}
/>
<TextInput
disabled
placeholder={"Текущий статус"}
label={"Текущий статус"}
value={DealStatusDictionary[deal.currentStatus as DealStatus]}/>
<Checkbox
label={"Сделка завершена"}
{...form.getInputProps('isCompleted')}
placeholder={"Введите телефон"}
label={"Телефон клиента"}
{...form.getInputProps('client.details.phoneNumber')}
/>
<Checkbox
label={"Сделка удалена"}
{...form.getInputProps('isDeleted')}
<TextInput
placeholder={"Введите email"}
label={"Email"}
{...form.getInputProps('client.details.email')}
/>
<TextInput
placeholder={"Введите адрес"}
label={"Адрес"}
{...form.getInputProps('client.details.address')}
/>
<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'}>
<Checkbox
label={"Сделка завершена"}
{...form.getInputProps('isCompleted')}
/>
<Checkbox
label={"Сделка удалена"}
{...form.getInputProps('isDeleted')}
/>
</Flex>
<Divider
orientation={'vertical'}
/>
<Group align={'center'} justify={'center'}>
<Button
color={"red"}
type={"reset"}
disabled={isEqual(initialValues, form.values)}
onClick={() => form.reset()}
>Отменить изменения</Button>
<Button
variant={"default"}
type={"submit"}
disabled={isEqual(initialValues, form.values)}
>Сохранить изменения</Button>
</Group>
</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.address')}
/>
<TextInput
placeholder={"Введите ИНН"}
label={"ИНН"}
{...form.getInputProps('client.details.inn')}
/>
</Fieldset>
<Group justify={"flex-end"} mt={"md"}>
<Button
color={"red"}
type={"reset"}
disabled={isEqual(initialValues, form.values)}
onClick={() => form.reset()}
>Отменить изменения</Button>
<Button
variant={"default"}
type={"submit"}
disabled={isEqual(initialValues, form.values)}
>Сохранить изменения</Button>
</Group>
</Flex>
</form>
)

View File

@@ -1,12 +1,14 @@
import {FC, useEffect, useState} from "react";
import styles from './LeadsPage.module.css';
import Board from "../../../components/Dnd/Board/Board.tsx";
import {DragDropContext} from "@hello-pangea/dnd";
import {DragDropContext, DropResult, OnDragEndResponder, ResponderProvided} 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";
import {modals} from "@mantine/modals";
import {DealSummaryReorderRequest} from "../../../client";
export const LeadsPage: FC = () => {
@@ -15,15 +17,18 @@ export const LeadsPage: FC = () => {
useEffect(() => {
setSummaries(summariesRaw);
}, [summariesRaw]);
const onDragEnd = () => {
// if (!result.destination) return;
//
// const newStatus = getDealStatusByName(
// result.destination.droppableId
// );
// const summaryId = parseInt(result.draggableId);
//
// return;
const onDragEnd = async (result: DropResult, provided: ResponderProvided) => {
const dealId = parseInt(result.draggableId);
const request: DealSummaryReorderRequest = {}
modals.openContextModal({
modal: 'enterDeadline',
title: "Необходимо указать дедлайн",
innerProps: {
onSubmit: (event) => console.log(event)
}
});
}
return (
<>