crap
This commit is contained in:
17
package.json
17
package.json
@@ -12,12 +12,13 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@hello-pangea/dnd": "^16.5.0",
|
||||
"@mantine/core": "^7.5.3",
|
||||
"@mantine/dates": "^7.5.3",
|
||||
"@mantine/dropzone": "^7.5.3",
|
||||
"@mantine/hooks": "^7.5.3",
|
||||
"@mantine/modals": "^7.5.3",
|
||||
"@mantine/notifications": "^7.5.3",
|
||||
"@mantine/core": "^7.6.1",
|
||||
"@mantine/dates": "^7.6.1",
|
||||
"@mantine/dropzone": "^7.6.1",
|
||||
"@mantine/form": "^7.6.1",
|
||||
"@mantine/hooks": "^7.6.1",
|
||||
"@mantine/modals": "^7.6.1",
|
||||
"@mantine/notifications": "^7.6.1",
|
||||
"@reduxjs/toolkit": "^2.2.1",
|
||||
"@tabler/icons-react": "^2.47.0",
|
||||
"@tanstack/react-query": "^5.22.2",
|
||||
@@ -27,11 +28,13 @@
|
||||
"axios": "^1.6.7",
|
||||
"clsx": "^2.1.0",
|
||||
"dayjs": "^1.11.10",
|
||||
"mantine-form-zod-resolver": "^1.1.0",
|
||||
"mantine-react-table": "^2.0.0-beta.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-redux": "^9.1.0",
|
||||
"reactflow": "^11.10.4"
|
||||
"reactflow": "^11.10.4",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.56",
|
||||
|
||||
@@ -9,7 +9,12 @@ export type { OpenAPIConfig } from './core/OpenAPI';
|
||||
|
||||
export type { AuthLoginRequest } from './models/AuthLoginRequest';
|
||||
export type { AuthLoginResponse } from './models/AuthLoginResponse';
|
||||
export type { DealChangeStatusRequest } from './models/DealChangeStatusRequest';
|
||||
export type { DealChangeStatusResponse } from './models/DealChangeStatusResponse';
|
||||
export type { DealCreateRequest } from './models/DealCreateRequest';
|
||||
export type { HTTPValidationError } from './models/HTTPValidationError';
|
||||
export type { ValidationError } from './models/ValidationError';
|
||||
|
||||
export { AuthService } from './services/AuthService';
|
||||
export { ClientService } from './services/ClientService';
|
||||
export { DealService } from './services/DealService';
|
||||
|
||||
9
src/client/models/DealChangeStatusRequest.ts
Normal file
9
src/client/models/DealChangeStatusRequest.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/* generated using openapi-typescript-codegen -- do no edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type DealChangeStatusRequest = {
|
||||
deal_id: number;
|
||||
new_status: number;
|
||||
};
|
||||
|
||||
8
src/client/models/DealChangeStatusResponse.ts
Normal file
8
src/client/models/DealChangeStatusResponse.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/* generated using openapi-typescript-codegen -- do no edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type DealChangeStatusResponse = {
|
||||
ok: boolean;
|
||||
};
|
||||
|
||||
8
src/client/models/DealCreateRequest.ts
Normal file
8
src/client/models/DealCreateRequest.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/* generated using openapi-typescript-codegen -- do no edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type DealCreateRequest = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
30
src/client/services/ClientService.ts
Normal file
30
src/client/services/ClientService.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/* generated using openapi-typescript-codegen -- do no edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||
import { OpenAPI } from '../core/OpenAPI';
|
||||
import { request as __request } from '../core/request';
|
||||
export class ClientService {
|
||||
/**
|
||||
* Search Clients
|
||||
* @returns any Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static searchClients({
|
||||
name,
|
||||
}: {
|
||||
name: string,
|
||||
}): CancelablePromise<any> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/client/search',
|
||||
query: {
|
||||
'name': name,
|
||||
},
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
52
src/client/services/DealService.ts
Normal file
52
src/client/services/DealService.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
/* generated using openapi-typescript-codegen -- do no edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { DealChangeStatusRequest } from '../models/DealChangeStatusRequest';
|
||||
import type { DealChangeStatusResponse } from '../models/DealChangeStatusResponse';
|
||||
import type { DealCreateRequest } from '../models/DealCreateRequest';
|
||||
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||
import { OpenAPI } from '../core/OpenAPI';
|
||||
import { request as __request } from '../core/request';
|
||||
export class DealService {
|
||||
/**
|
||||
* Create
|
||||
* @returns any Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static createDealCreatePost({
|
||||
requestBody,
|
||||
}: {
|
||||
requestBody: DealCreateRequest,
|
||||
}): CancelablePromise<any> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/deal/create',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Change Status
|
||||
* @returns DealChangeStatusResponse Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static changeStatusDealChangeStatusPost({
|
||||
requestBody,
|
||||
}: {
|
||||
requestBody: DealChangeStatusRequest,
|
||||
}): CancelablePromise<DealChangeStatusResponse> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/deal/changeStatus',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import {FC} from "react";
|
||||
import styles from './Board.module.css';
|
||||
import {Divider, Text, Title} from '@mantine/core';
|
||||
import {Draggable, Droppable} from "@hello-pangea/dnd";
|
||||
import CreateLeadButton from "../CreateLeadButton/CreateLeadButton.tsx";
|
||||
import CreateDealButton from "../CreateDealButton/CreateDealButton.tsx";
|
||||
|
||||
type Props = {
|
||||
droppableId: string;
|
||||
@@ -21,16 +21,16 @@ export const Board: FC<Props> = ({droppableId, title, withCreateButton = false})
|
||||
<Divider size={"xl"} my={10} color={"blue"}/>
|
||||
</div>
|
||||
<Droppable droppableId={droppableId}>
|
||||
{(provided, snapshot) => (
|
||||
{(provided) => (
|
||||
<div ref={provided.innerRef} className={styles["items-list"]}>
|
||||
{withCreateButton &&
|
||||
<CreateLeadButton
|
||||
<CreateDealButton
|
||||
|
||||
onClick={() => {
|
||||
}}
|
||||
/>}
|
||||
<Draggable draggableId={droppableId + '1'} index={1}>
|
||||
{(provided, snapshot) => (
|
||||
{(provided) => (
|
||||
<div {...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
ref={provided.innerRef}
|
||||
|
||||
31
src/components/Dnd/CreateDealButton/CreateDealButton.tsx
Normal file
31
src/components/Dnd/CreateDealButton/CreateDealButton.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import {FC, useState} from "react";
|
||||
|
||||
import styles from './CreateDealButton.module.css';
|
||||
import {Button, rem, Text, TextInput, Transition} from '@mantine/core';
|
||||
import CreateDealFrom from "../CreateDealForm/CreateDealFrom.tsx";
|
||||
|
||||
type Props = {
|
||||
onClick: () => void;
|
||||
}
|
||||
const CreateDealButton: FC<Props> = ({onClick}) => {
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
|
||||
return (
|
||||
<div className={styles['container']}
|
||||
onClick={() => {
|
||||
if (isCreating) return;
|
||||
setIsCreating(prevState => !prevState)
|
||||
}}
|
||||
>
|
||||
|
||||
<Text>Быстрое добавление</Text>
|
||||
<CreateDealFrom
|
||||
onCancel={() => {
|
||||
}}
|
||||
onSubmit={() => {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default CreateDealButton;
|
||||
15
src/components/Dnd/CreateDealForm/CreateDealForm.module.css
Normal file
15
src/components/Dnd/CreateDealForm/CreateDealForm.module.css
Normal file
@@ -0,0 +1,15 @@
|
||||
.inputs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.inputs * {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
gap: rem(10);
|
||||
}
|
||||
|
||||
83
src/components/Dnd/CreateDealForm/CreateDealFrom.tsx
Normal file
83
src/components/Dnd/CreateDealForm/CreateDealFrom.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
import {Button, rem, Textarea, TextInput} from "@mantine/core";
|
||||
import {QuickDeal} from "../../../types/QuickDeal.ts";
|
||||
import {FC} from "react";
|
||||
import {useForm} from "@mantine/form";
|
||||
import styles from './CreateDealForm.module.css';
|
||||
import ClientSelect from "../../Selects/ClientSelect/ClientSelect.tsx";
|
||||
import {DateTimePicker} from "@mantine/dates";
|
||||
|
||||
type Props = {
|
||||
onSubmit: (quickDeal: QuickDeal) => void
|
||||
onCancel: () => void;
|
||||
}
|
||||
const CreateDealFrom: FC<Props> = ({onSubmit, onCancel}) => {
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
name: '',
|
||||
clientName: '',
|
||||
clientAddress: '',
|
||||
comment: '',
|
||||
acceptance_date: new Date()
|
||||
}
|
||||
});
|
||||
return (
|
||||
<form
|
||||
style={{width: '100%'}}
|
||||
onSubmit={form.onSubmit((values) => console.log(values))}
|
||||
>
|
||||
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
gap: rem(10),
|
||||
padding: rem(10)
|
||||
}}>
|
||||
<div className={styles['inputs']}>
|
||||
<TextInput
|
||||
placeholder={'Название'}
|
||||
{...form.getInputProps('name')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles['inputs']}>
|
||||
<ClientSelect
|
||||
withAddress
|
||||
nameRestProps={form.getInputProps('clientName')}
|
||||
addressRestProps={form.getInputProps('clientAddress')}
|
||||
onSelect={() => {
|
||||
}}/>
|
||||
</div>
|
||||
|
||||
<div className={styles['inputs']}>
|
||||
<Textarea
|
||||
autosize
|
||||
placeholder={'Комментарий'}
|
||||
minRows={2}
|
||||
maxRows={4}
|
||||
{...form.getInputProps('comment')}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles['inputs']}>
|
||||
<DateTimePicker
|
||||
placeholder={'Дата приемки'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<div className={styles['buttons']}>
|
||||
<Button
|
||||
type={'submit'}
|
||||
>Добавить</Button>
|
||||
<Button
|
||||
variant={'outline'}
|
||||
onClick={() => onCancel()}
|
||||
>Отменить</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default CreateDealFrom;
|
||||
@@ -1,60 +0,0 @@
|
||||
import React, {FC, useState} from "react";
|
||||
|
||||
import styles from './CreateLeadButton.module.css';
|
||||
import {Button, Center, rem, Text, TextInput, Transition} from '@mantine/core';
|
||||
|
||||
type Props = {
|
||||
onClick: () => void;
|
||||
}
|
||||
const CreateLeadButton: FC<Props> = ({onClick}) => {
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
const [showButton, setShowButton] = useState(true);
|
||||
console.log(`isCreating: ${isCreating}`)
|
||||
console.log(`showButton: ${showButton}`)
|
||||
return (
|
||||
<div className={styles['container']}
|
||||
onClick={() => {
|
||||
if (isCreating) return;
|
||||
setIsCreating(prevState => !prevState)
|
||||
setShowButton(false);
|
||||
}}
|
||||
>
|
||||
|
||||
{(!isCreating && showButton) &&
|
||||
<Text>Быстрое добавление</Text>
|
||||
}
|
||||
<Transition
|
||||
mounted={isCreating}
|
||||
transition={'scale-y'}
|
||||
duration={300}
|
||||
// onExited={()=>setShowButton(true)}
|
||||
keepMounted
|
||||
>
|
||||
{(styles) => <div style={{...styles, width: '100%'}}>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
gap: rem(10),
|
||||
padding: rem(10)
|
||||
}}>
|
||||
<div style={{display: "flex", flexDirection: "column", width: '100%'}}>
|
||||
<TextInput placeholder={'Название'} w={'100%'}/>
|
||||
|
||||
</div>
|
||||
|
||||
<div style={{display: "flex", flexDirection: "column", width: '100%'}}>
|
||||
<TextInput placeholder={'Компания: название'} w={'100%'}/>
|
||||
<TextInput placeholder={'Компания: адрес'} w={'100%'}/>
|
||||
</div>
|
||||
<div style={{gap:rem(10), display:"flex"}}>
|
||||
<Button>Добавить</Button>
|
||||
<Button variant={'outline'} onClick={()=>setIsCreating(false)}>Отменить</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>}
|
||||
</Transition>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default CreateLeadButton;
|
||||
87
src/components/Selects/ClientSelect/ClientSelect.tsx
Normal file
87
src/components/Selects/ClientSelect/ClientSelect.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import {useDebouncedValue} from "@mantine/hooks";
|
||||
import {Autocomplete, AutocompleteProps, TextInput, TextInputProps} from "@mantine/core";
|
||||
import {FC, useEffect, useState} from "react";
|
||||
import {Client} from "../../../types/Client.ts";
|
||||
import {ClientService} from "../../../client";
|
||||
|
||||
type Props = {
|
||||
onSelect?: (client: Client) => void;
|
||||
withAddress?: boolean;
|
||||
nameRestProps?: AutocompleteProps;
|
||||
addressRestProps?: TextInputProps;
|
||||
}
|
||||
const ClientSelect: FC<Props> = ({onSelect, addressRestProps, nameRestProps, withAddress = false}) => {
|
||||
const [value, setValue] = useState('');
|
||||
const [debouncedValue] = useDebouncedValue(value, 200);
|
||||
|
||||
// const [isLoading, setIsLoading] = useState(false);
|
||||
const [clients, setClients] = useState<Client[]>([])
|
||||
const [selectedClient, selectClient] = useState<Client>();
|
||||
|
||||
const handleChange = (value: string) => {
|
||||
setClients([]);
|
||||
setValue(value);
|
||||
}
|
||||
const handleDebouncedChange = () => {
|
||||
if (!value.trim()) return;
|
||||
// setIsLoading(true);
|
||||
ClientService.searchClients({name: value}).then(({clients}) => {
|
||||
setClients(clients);
|
||||
// setIsLoading(false);
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
handleDebouncedChange();
|
||||
}, [debouncedValue]);
|
||||
|
||||
useEffect(() => {
|
||||
selectClient(clients.find(client =>
|
||||
client.name.toLowerCase().trim() == value.toLowerCase().trim())
|
||||
||
|
||||
{
|
||||
name: value,
|
||||
id: -1,
|
||||
address: ''
|
||||
});
|
||||
}, [value]);
|
||||
useEffect(() => {
|
||||
if (!selectedClient) return;
|
||||
if (onSelect) onSelect(selectedClient);
|
||||
if (nameRestProps?.onChange) nameRestProps.onChange(selectedClient.name);
|
||||
if (addressRestProps?.onChange) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
addressRestProps.onChange(selectedClient.address);
|
||||
}
|
||||
}, [selectedClient]);
|
||||
return (
|
||||
<>
|
||||
<Autocomplete
|
||||
{...nameRestProps}
|
||||
placeholder={'Клиент: название'}
|
||||
onChange={handleChange}
|
||||
value={value}
|
||||
data={clients.map(client => client.name)}
|
||||
styles={withAddress ? {
|
||||
input: {
|
||||
borderBottomLeftRadius: 0,
|
||||
borderBottomRightRadius: 0
|
||||
}
|
||||
} : {}}
|
||||
/>
|
||||
|
||||
{withAddress &&
|
||||
<TextInput
|
||||
placeholder={'Клиент: адрес'}
|
||||
value={selectedClient?.address}
|
||||
onChange={event => {
|
||||
selectClient(prevState => prevState && {...prevState, address: event.target.value})
|
||||
}}
|
||||
/>
|
||||
}
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
export default ClientSelect;
|
||||
10
src/main.tsx
10
src/main.tsx
@@ -1,4 +1,3 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import {RouterProvider, createRouter} from '@tanstack/react-router'
|
||||
import {routeTree} from './routeTree.gen'
|
||||
@@ -9,11 +8,14 @@ import {store} from "./redux/store.ts";
|
||||
|
||||
import '@mantine/core/styles.css';
|
||||
import '@mantine/notifications/styles.css';
|
||||
import '@mantine/dates/styles.css';
|
||||
import 'dayjs/locale/ru';
|
||||
|
||||
import './main.css';
|
||||
import {Notifications} from "@mantine/notifications";
|
||||
import {ModalsProvider} from "@mantine/modals";
|
||||
import {OpenAPI} from "./client";
|
||||
import PageWrapper from "./pages/PageWrapper/PageWrapper.tsx";
|
||||
import {DatesProvider} from "@mantine/dates";
|
||||
|
||||
// Configuring router
|
||||
const router = createRouter({routeTree})
|
||||
@@ -35,10 +37,12 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<MantineProvider defaultColorScheme={"dark"}>
|
||||
<ModalsProvider>
|
||||
<DatesProvider settings={{locale: 'ru'}}>
|
||||
|
||||
<RouterProvider router={router}/>
|
||||
<Notifications/>
|
||||
</DatesProvider>
|
||||
|
||||
<Notifications/>
|
||||
</ModalsProvider>
|
||||
</MantineProvider>
|
||||
</QueryClientProvider>
|
||||
|
||||
@@ -3,12 +3,15 @@ import styles from './LeadsPage.module.css';
|
||||
import Board from "../../../components/Dnd/Board/Board.tsx";
|
||||
import {Button, TextInput} from "@mantine/core";
|
||||
import {DragDropContext} from "@hello-pangea/dnd";
|
||||
import ClientSelect from "../../../components/Selects/ClientSelect/ClientSelect.tsx";
|
||||
import {DateTimePicker} from "@mantine/dates";
|
||||
|
||||
export const LeadsPage: FC = () => {
|
||||
|
||||
return (
|
||||
<div className={styles['container']}>
|
||||
<div className={styles['header']}>
|
||||
|
||||
<TextInput
|
||||
radius={0}
|
||||
placeholder={"Поиск и фильтры"}
|
||||
@@ -18,7 +21,7 @@ export const LeadsPage: FC = () => {
|
||||
<Button
|
||||
radius={0}
|
||||
color={"gray"}
|
||||
variant={"light"}
|
||||
variant={'default'}
|
||||
className={styles['header-button']}
|
||||
>Поиск</Button>
|
||||
</div>
|
||||
|
||||
5
src/types/Client.ts
Normal file
5
src/types/Client.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export type Client = {
|
||||
id?: number;
|
||||
name: string;
|
||||
address: string;
|
||||
}
|
||||
8
src/types/QuickDeal.ts
Normal file
8
src/types/QuickDeal.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import {Client} from "./Client.ts";
|
||||
|
||||
export type QuickDeal = {
|
||||
name: string;
|
||||
client: Client
|
||||
comment: string;
|
||||
acceptance_date: string;
|
||||
}
|
||||
Reference in New Issue
Block a user