This commit is contained in:
2024-03-17 03:39:38 +03:00
parent e2962f3be2
commit cc14105276
24 changed files with 4218 additions and 42 deletions

3954
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -9,11 +9,15 @@ export type { OpenAPIConfig } from './core/OpenAPI';
export type { AuthLoginRequest } from './models/AuthLoginRequest';
export type { AuthLoginResponse } from './models/AuthLoginResponse';
export type { ClientDetailsSchema } from './models/ClientDetailsSchema';
export type { ClientGetAllResponse } from './models/ClientGetAllResponse';
export type { ClientSchema } from './models/ClientSchema';
export type { ClientUpdateDetailsRequest } from './models/ClientUpdateDetailsRequest';
export type { DealChangeStatusRequest } from './models/DealChangeStatusRequest';
export type { DealChangeStatusResponse } from './models/DealChangeStatusResponse';
export type { DealCreateRequest } from './models/DealCreateRequest';
export type { DealCreateResponse } from './models/DealCreateResponse';
export type { DealQuickCreateRequest } from './models/DealQuickCreateRequest';
export type { DealQuickCreateResponse } from './models/DealQuickCreateResponse';
export type { HTTPValidationError } from './models/HTTPValidationError';
export type { ValidationError } from './models/ValidationError';

View File

@@ -0,0 +1,11 @@
/* generated using openapi-typescript-codegen -- do no edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type ClientDetailsSchema = {
address?: (string | null);
phone_number?: (string | null);
inn?: (number | null);
email?: (string | null);
};

View File

@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do no edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { ClientSchema } from './ClientSchema';
export type ClientGetAllResponse = {
clients: Array<ClientSchema>;
};

View File

@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do no edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type ClientSchema = {
id: number;
name: string;
};

View File

@@ -0,0 +1,10 @@
/* generated using openapi-typescript-codegen -- do no edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { ClientDetailsSchema } from './ClientDetailsSchema';
export type ClientUpdateDetailsRequest = {
client_id: number;
details: ClientDetailsSchema;
};

View File

@@ -0,0 +1,12 @@
/* generated using openapi-typescript-codegen -- do no edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type DealQuickCreateRequest = {
name: string;
client_name: string;
client_address: string;
comment: string;
acceptance_date: string;
};

View File

@@ -0,0 +1,8 @@
/* generated using openapi-typescript-codegen -- do no edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type DealQuickCreateResponse = {
deal_id: number;
};

View File

@@ -2,6 +2,8 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { ClientGetAllResponse } from '../models/ClientGetAllResponse';
import type { ClientUpdateDetailsRequest } from '../models/ClientUpdateDetailsRequest';
import type { CancelablePromise } from '../core/CancelablePromise';
import { OpenAPI } from '../core/OpenAPI';
import { request as __request } from '../core/request';
@@ -27,4 +29,35 @@ export class ClientService {
},
});
}
/**
* Update Client Details
* @returns any Successful Response
* @throws ApiError
*/
public static updateClientDetailsClientUpdateDetailsPost({
requestBody,
}: {
requestBody: ClientUpdateDetailsRequest,
}): CancelablePromise<any> {
return __request(OpenAPI, {
method: 'POST',
url: '/client/update-details',
body: requestBody,
mediaType: 'application/json',
errors: {
422: `Validation Error`,
},
});
}
/**
* Get All Clients
* @returns ClientGetAllResponse Successful Response
* @throws ApiError
*/
public static getAllClients(): CancelablePromise<ClientGetAllResponse> {
return __request(OpenAPI, {
method: 'GET',
url: '/client/get-all',
});
}
}

View File

@@ -5,8 +5,8 @@
import type { DealChangeStatusRequest } from '../models/DealChangeStatusRequest';
import type { DealChangeStatusResponse } from '../models/DealChangeStatusResponse';
import type { DealCreateRequest } from '../models/DealCreateRequest';
import type { DealCreateResponse } from '../models/DealCreateResponse';
import type { DealQuickCreateRequest } from '../models/DealQuickCreateRequest';
import type { DealQuickCreateResponse } from '../models/DealQuickCreateResponse';
import type { CancelablePromise } from '../core/CancelablePromise';
import { OpenAPI } from '../core/OpenAPI';
import { request as __request } from '../core/request';
@@ -33,14 +33,14 @@ export class DealService {
}
/**
* Quick Create
* @returns DealCreateResponse Successful Response
* @returns DealQuickCreateResponse Successful Response
* @throws ApiError
*/
public static quickCreateDealQuickCreatePost({
requestBody,
}: {
requestBody: DealQuickCreateRequest,
}): CancelablePromise<DealCreateResponse> {
}): CancelablePromise<DealQuickCreateResponse> {
return __request(OpenAPI, {
method: 'POST',
url: '/deal/quickCreate',

View File

@@ -0,0 +1,14 @@
.header {
display: flex;
flex-direction: row;
}
.header-input {
flex: 1;
}
.header-button {
height: 100%;
width: 10%;
min-width: 5rem;
}

View File

@@ -0,0 +1,25 @@
import styles from './Header.module.css';
import {Button, TextInput} from "@mantine/core";
import {FC} from "react";
const Header: FC = ()=>{
return (
<div className={styles['header']}>
<TextInput
radius={0}
placeholder={"Поиск и фильтры"}
size={"xl"}
className={styles['header-input']}
/>
<Button
radius={0}
color={"gray"}
variant={'default'}
className={styles['header-button']}
>Поиск</Button>
</div>
)
}
export default Header;

View File

@@ -1,5 +1,5 @@
import {Center, Image, rem, Stack, Tooltip, UnstyledButton, useMantineColorScheme} from '@mantine/core';
import {IconCash, IconHome2, IconLogout, IconMoon, IconSun,} from '@tabler/icons-react';
import {IconCash, IconHome2, IconLogout, IconMan, IconMoon, IconSun,} from '@tabler/icons-react';
import classes from './Navbar.module.css';
import {useAppDispatch} from "../../redux/store.ts";
import {logout} from "../../features/authSlice.ts";
@@ -40,7 +40,11 @@ const mockdata = [
label: 'Сделки',
href: '/leads'
},
{
icon: IconMan,
label: 'Клиенты',
href: '/clients'
}
];
export function Navbar() {
@@ -81,7 +85,8 @@ export function Navbar() {
</div>
<Stack justify="center" gap={0}>
<NavbarLink label={"Сменить тему"} onClick={toggleColorScheme} icon={colorScheme == "dark" ? IconSun : IconMoon} href={"#"} index={-1}/>
<NavbarLink label={"Сменить тему"} onClick={toggleColorScheme}
icon={colorScheme == "dark" ? IconSun : IconMoon} href={"#"} index={-1}/>
<NavbarLink index={-1} href={"#"} onClick={onLogoutClick} icon={IconLogout} label="Выйти"/>
</Stack>
</nav>

View File

@@ -0,0 +1,4 @@
.container {
/*background: rebeccapurple;*/
flex: 1;
}

View File

@@ -0,0 +1,17 @@
import {FC} from "react";
import styles from './ClientsPage.module.css';
import ClientsTable from "./components/ClientsTable/ClientsTable.tsx";
import {ClientService} from "../../client";
import {useQuery} from "@tanstack/react-query";
const ClientsPage: FC = () => {
const {data} = useQuery({queryKey: ['clients'], queryFn: ClientService.getAllClients})
return (
<div className={styles['container']}>
<ClientsTable data={data?.clients || []}/>
</div>
)
}
export default ClientsPage;

View File

@@ -0,0 +1,53 @@
import {Checkbox, rem, Table} from "@mantine/core";
import {columns} from "./columns.tsx";
import React, {FC} from "react";
import {Client} from "../../../../types/Client.ts";
import {IconEdit} from "@tabler/icons-react";
import {notifications} from "../../../../shared/lib/notifications.ts";
type Props = {
data: Client[];
}
const ClientsTable: FC<Props> = ({data}) => {
return (
<Table.ScrollContainer minWidth={rem(50)}>
<Table striped>
<Table.Thead>
<Table.Tr>
<Table.Th/>
{columns.map(column => (
<Table.Th>{column.header}</Table.Th>
))}
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{data.map(client => (
<Table.Tr key={client.name}>
<Table.Td>
<IconEdit
onClick={() => {
notifications.success({message: `Редактирую ${client.id}`})
}}
style={{cursor: 'pointer'}}
/>
</Table.Td>
{columns.map(column => (
<Table.Td>{client[column.accessorKey]}</Table.Td>
))}
</Table.Tr>
))}
</Table.Tbody>
</Table>
</Table.ScrollContainer>
)
}
export default ClientsTable;

View File

@@ -0,0 +1,12 @@
import {Client} from "../../../../types/Client.ts";
type Column = {
accessorKey: keyof Client;
header: string;
}
export const columns: Column[] = [
{
accessorKey: 'name',
header: 'Название'
}
]

View File

@@ -4,20 +4,6 @@
flex-direction: column;
}
.header {
display: flex;
flex-direction: row;
}
.header-input {
flex: 1;
}
.header-button {
height: 100%;
width: 10%;
min-width: 5rem;
}
.search-input {

View File

@@ -3,33 +3,16 @@ 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 {useAppDispatch} from "../../../redux/store.ts";
import {setIsLoading} from "../../../features/uiSlice.ts";
export const LeadsPage: FC = () => {
const dispatch = useAppDispatch();
useEffect(() => {
dispatch(setIsLoading(true));
// dispatch(setIsLoading(true));
}, []);
return (
<>
<div className={styles['container']}>
<div className={styles['header']}>
<TextInput
radius={0}
placeholder={"Поиск и фильтры"}
size={"xl"}
className={styles['header-input']}
/>
<Button
radius={0}
color={"gray"}
variant={'default'}
className={styles['header-button']}
>Поиск</Button>
</div>
<div className={styles['boards']}>
<DragDropContext onDragEnd={() => {
}}>

View File

@@ -1,8 +1,9 @@
import {FC, ReactNode} from "react";
import {Flex} from "@mantine/core";
import {Flex, rem} from "@mantine/core";
import {Navbar} from "../../components/Navbar/Navbar.tsx";
import {useSelector} from "react-redux";
import {RootState} from "../../redux/store.ts";
import Header from "../../components/Header/Header.tsx";
export type Props = {
children: ReactNode;
@@ -10,10 +11,20 @@ export type Props = {
const PageWrapper: FC<Props> = ({children}) => {
const authState = useSelector((state: RootState) => state.auth);
return (<Flex style={{flex: 1}}>
{authState.isAuthorized &&
<Navbar/>
}
<div style={{flex: 1, display: 'flex', flexDirection: 'column'}}>
<Header/>
<div style={{padding:rem(20), flex:1}}>
{children}
</div>
</div>
</Flex>
)
}

View File

@@ -19,6 +19,7 @@ import { Route as rootRoute } from './routes/__root'
const TestLazyImport = createFileRoute('/test')()
const LoginLazyImport = createFileRoute('/login')()
const LeadsLazyImport = createFileRoute('/leads')()
const ClientsLazyImport = createFileRoute('/clients')()
const IndexLazyImport = createFileRoute('/')()
// Create/Update Routes
@@ -38,6 +39,11 @@ const LeadsLazyRoute = LeadsLazyImport.update({
getParentRoute: () => rootRoute,
} as any).lazy(() => import('./routes/leads.lazy').then((d) => d.Route))
const ClientsLazyRoute = ClientsLazyImport.update({
path: '/clients',
getParentRoute: () => rootRoute,
} as any).lazy(() => import('./routes/clients.lazy').then((d) => d.Route))
const IndexLazyRoute = IndexLazyImport.update({
path: '/',
getParentRoute: () => rootRoute,
@@ -51,6 +57,10 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof IndexLazyImport
parentRoute: typeof rootRoute
}
'/clients': {
preLoaderRoute: typeof ClientsLazyImport
parentRoute: typeof rootRoute
}
'/leads': {
preLoaderRoute: typeof LeadsLazyImport
parentRoute: typeof rootRoute
@@ -70,6 +80,7 @@ declare module '@tanstack/react-router' {
export const routeTree = rootRoute.addChildren([
IndexLazyRoute,
ClientsLazyRoute,
LeadsLazyRoute,
LoginLazyRoute,
TestLazyRoute,

View File

@@ -0,0 +1,6 @@
import {createLazyFileRoute} from "@tanstack/react-router";
import ClientsPage from "../pages/ClientsPage/ClientsPage.tsx";
export const Route = createLazyFileRoute('/clients')({
component: ClientsPage
})

View File

@@ -1,5 +1,4 @@
export type Client = {
id?: number;
name: string;
address: string;
}