crap
This commit is contained in:
@@ -26,8 +26,10 @@
|
|||||||
"@tanstack/router-devtools": "^1.16.6",
|
"@tanstack/router-devtools": "^1.16.6",
|
||||||
"@tanstack/router-vite-plugin": "^1.16.5",
|
"@tanstack/router-vite-plugin": "^1.16.5",
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
|
"classnames": "^2.5.1",
|
||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
|
"dot-object": "^2.1.4",
|
||||||
"mantine-form-zod-resolver": "^1.1.0",
|
"mantine-form-zod-resolver": "^1.1.0",
|
||||||
"mantine-react-table": "^2.0.0-beta.0",
|
"mantine-react-table": "^2.0.0-beta.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
@@ -37,6 +39,7 @@
|
|||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/dot-object": "^2",
|
||||||
"@types/react": "^18.2.56",
|
"@types/react": "^18.2.56",
|
||||||
"@types/react-dom": "^18.2.19",
|
"@types/react-dom": "^18.2.19",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.0.2",
|
"@typescript-eslint/eslint-plugin": "^7.0.2",
|
||||||
|
|||||||
@@ -18,9 +18,20 @@ export type { DealChangeStatusResponse } from './models/DealChangeStatusResponse
|
|||||||
export type { DealCreateRequest } from './models/DealCreateRequest';
|
export type { DealCreateRequest } from './models/DealCreateRequest';
|
||||||
export type { DealQuickCreateRequest } from './models/DealQuickCreateRequest';
|
export type { DealQuickCreateRequest } from './models/DealQuickCreateRequest';
|
||||||
export type { DealQuickCreateResponse } from './models/DealQuickCreateResponse';
|
export type { DealQuickCreateResponse } from './models/DealQuickCreateResponse';
|
||||||
|
export type { DealSummary } from './models/DealSummary';
|
||||||
|
export type { DealSummaryResponse } from './models/DealSummaryResponse';
|
||||||
export type { HTTPValidationError } from './models/HTTPValidationError';
|
export type { HTTPValidationError } from './models/HTTPValidationError';
|
||||||
|
export type { ServiceCategorySchema } from './models/ServiceCategorySchema';
|
||||||
|
export type { ServiceCreateCategoryRequest } from './models/ServiceCreateCategoryRequest';
|
||||||
|
export type { ServiceCreateCategoryResponse } from './models/ServiceCreateCategoryResponse';
|
||||||
|
export type { ServiceCreateRequest } from './models/ServiceCreateRequest';
|
||||||
|
export type { ServiceCreateResponse } from './models/ServiceCreateResponse';
|
||||||
|
export type { ServiceGetAllCategoriesResponse } from './models/ServiceGetAllCategoriesResponse';
|
||||||
|
export type { ServiceGetAllResponse } from './models/ServiceGetAllResponse';
|
||||||
|
export type { ServiceSchema } from './models/ServiceSchema';
|
||||||
export type { ValidationError } from './models/ValidationError';
|
export type { ValidationError } from './models/ValidationError';
|
||||||
|
|
||||||
export { AuthService } from './services/AuthService';
|
export { AuthService } from './services/AuthService';
|
||||||
export { ClientService } from './services/ClientService';
|
export { ClientService } from './services/ClientService';
|
||||||
export { DealService } from './services/DealService';
|
export { DealService } from './services/DealService';
|
||||||
|
export { ServiceService } from './services/ServiceService';
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
/* istanbul ignore file */
|
/* istanbul ignore file */
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
import type { ClientDetailsSchema } from './ClientDetailsSchema';
|
||||||
export type ClientSchema = {
|
export type ClientSchema = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
details?: (ClientDetailsSchema | null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
12
src/client/models/DealSummary.ts
Normal file
12
src/client/models/DealSummary.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type DealSummary = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
client_name: string;
|
||||||
|
changed_at: string;
|
||||||
|
status: number;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/DealSummaryResponse.ts
Normal file
9
src/client/models/DealSummaryResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { DealSummary } from './DealSummary';
|
||||||
|
export type DealSummaryResponse = {
|
||||||
|
summaries: Array<DealSummary>;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/ServiceCategorySchema.ts
Normal file
9
src/client/models/ServiceCategorySchema.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type ServiceCategorySchema = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/ServiceCreateCategoryRequest.ts
Normal file
9
src/client/models/ServiceCreateCategoryRequest.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { ServiceCategorySchema } from './ServiceCategorySchema';
|
||||||
|
export type ServiceCreateCategoryRequest = {
|
||||||
|
category: ServiceCategorySchema;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/ServiceCreateCategoryResponse.ts
Normal file
9
src/client/models/ServiceCreateCategoryResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type ServiceCreateCategoryResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/ServiceCreateRequest.ts
Normal file
9
src/client/models/ServiceCreateRequest.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { ServiceSchema } from './ServiceSchema';
|
||||||
|
export type ServiceCreateRequest = {
|
||||||
|
service: ServiceSchema;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/ServiceCreateResponse.ts
Normal file
9
src/client/models/ServiceCreateResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type ServiceCreateResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/ServiceGetAllCategoriesResponse.ts
Normal file
9
src/client/models/ServiceGetAllCategoriesResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { ServiceCategorySchema } from './ServiceCategorySchema';
|
||||||
|
export type ServiceGetAllCategoriesResponse = {
|
||||||
|
categories: Array<ServiceCategorySchema>;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/ServiceGetAllResponse.ts
Normal file
9
src/client/models/ServiceGetAllResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { ServiceSchema } from './ServiceSchema';
|
||||||
|
export type ServiceGetAllResponse = {
|
||||||
|
services: Array<ServiceSchema>;
|
||||||
|
};
|
||||||
|
|
||||||
12
src/client/models/ServiceSchema.ts
Normal file
12
src/client/models/ServiceSchema.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { ServiceCategorySchema } from './ServiceCategorySchema';
|
||||||
|
export type ServiceSchema = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
category: ServiceCategorySchema;
|
||||||
|
price: number;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -7,6 +7,7 @@ import type { DealChangeStatusResponse } from '../models/DealChangeStatusRespons
|
|||||||
import type { DealCreateRequest } from '../models/DealCreateRequest';
|
import type { DealCreateRequest } from '../models/DealCreateRequest';
|
||||||
import type { DealQuickCreateRequest } from '../models/DealQuickCreateRequest';
|
import type { DealQuickCreateRequest } from '../models/DealQuickCreateRequest';
|
||||||
import type { DealQuickCreateResponse } from '../models/DealQuickCreateResponse';
|
import type { DealQuickCreateResponse } from '../models/DealQuickCreateResponse';
|
||||||
|
import type { DealSummaryResponse } from '../models/DealSummaryResponse';
|
||||||
import type { CancelablePromise } from '../core/CancelablePromise';
|
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||||
import { OpenAPI } from '../core/OpenAPI';
|
import { OpenAPI } from '../core/OpenAPI';
|
||||||
import { request as __request } from '../core/request';
|
import { request as __request } from '../core/request';
|
||||||
@@ -71,4 +72,15 @@ export class DealService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Get Summary
|
||||||
|
* @returns DealSummaryResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static getDealSummaries(): CancelablePromise<DealSummaryResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/deal/summaries',
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
77
src/client/services/ServiceService.ts
Normal file
77
src/client/services/ServiceService.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { ServiceCreateCategoryRequest } from '../models/ServiceCreateCategoryRequest';
|
||||||
|
import type { ServiceCreateCategoryResponse } from '../models/ServiceCreateCategoryResponse';
|
||||||
|
import type { ServiceCreateRequest } from '../models/ServiceCreateRequest';
|
||||||
|
import type { ServiceCreateResponse } from '../models/ServiceCreateResponse';
|
||||||
|
import type { ServiceGetAllCategoriesResponse } from '../models/ServiceGetAllCategoriesResponse';
|
||||||
|
import type { ServiceGetAllResponse } from '../models/ServiceGetAllResponse';
|
||||||
|
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||||
|
import { OpenAPI } from '../core/OpenAPI';
|
||||||
|
import { request as __request } from '../core/request';
|
||||||
|
export class ServiceService {
|
||||||
|
/**
|
||||||
|
* Get All
|
||||||
|
* @returns ServiceGetAllResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static getAllServices(): CancelablePromise<ServiceGetAllResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/service/get-all',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create
|
||||||
|
* @returns ServiceCreateResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static createService({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: ServiceCreateRequest,
|
||||||
|
}): CancelablePromise<ServiceCreateResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/service/create',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get All Categories
|
||||||
|
* @returns ServiceGetAllCategoriesResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static getAllServiceCategories(): CancelablePromise<ServiceGetAllCategoriesResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/service/categories/get-all',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create Category
|
||||||
|
* @returns ServiceCreateCategoryResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static createServiceCategory({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: ServiceCreateCategoryRequest,
|
||||||
|
}): CancelablePromise<ServiceCreateCategoryResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/service/categories/create',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/components/BaseTable/BaseTable.tsx
Normal file
49
src/components/BaseTable/BaseTable.tsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import {
|
||||||
|
MantineReactTable,
|
||||||
|
MRT_ColumnDef,
|
||||||
|
MRT_RowData,
|
||||||
|
MRT_Table,
|
||||||
|
MRT_TableInstance,
|
||||||
|
MRT_TableOptions,
|
||||||
|
useMantineReactTable
|
||||||
|
} from "mantine-react-table";
|
||||||
|
import {MRT_Localization_RU} from "mantine-react-table/locales/ru/index.cjs";
|
||||||
|
import {forwardRef, useEffect, useImperativeHandle} from 'react';
|
||||||
|
|
||||||
|
type Props<T extends Record<string, any>, K extends keyof T> = {
|
||||||
|
data: T[],
|
||||||
|
columns: MRT_ColumnDef<T>[],
|
||||||
|
restProps?: MRT_TableOptions<T>,
|
||||||
|
striped?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Экспортируем тип рефа, чтобы он мог быть использован в других компонентах
|
||||||
|
export type BaseTableRef<T extends MRT_RowData> = {
|
||||||
|
getTable: () => MRT_TableInstance<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const BaseTable = forwardRef<BaseTableRef<any>, Props<any>>((props, ref) => {
|
||||||
|
const {data, columns, restProps, striped} = props;
|
||||||
|
|
||||||
|
const table = useMantineReactTable({
|
||||||
|
localization: MRT_Localization_RU,
|
||||||
|
enablePagination: false,
|
||||||
|
data,
|
||||||
|
columns,
|
||||||
|
mantineTableProps: {
|
||||||
|
striped: striped
|
||||||
|
},
|
||||||
|
enableTopToolbar: false,
|
||||||
|
...restProps,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Используем useImperativeHandle для определения, что будет доступно через ref
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
// Предполагаем, что есть метод getTable в table, который мы хотим выставить
|
||||||
|
getTable: () => table
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
return <MantineReactTable table={table}/>;
|
||||||
|
});
|
||||||
@@ -21,4 +21,20 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
/*background-color: red;*/
|
/*background-color: red;*/
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.items-list::after {
|
||||||
|
height: 5rem;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
.items-list-drag-over {
|
||||||
|
@mixin light {
|
||||||
|
background-color: var(--mantine-color-gray-1);
|
||||||
|
}
|
||||||
|
@mixin dark {
|
||||||
|
background-color: var(--mantine-color-dark-5);
|
||||||
|
|
||||||
|
}
|
||||||
|
border-radius: var(--item-border-radius);
|
||||||
}
|
}
|
||||||
@@ -3,14 +3,18 @@ import styles from './Board.module.css';
|
|||||||
import {Divider, Text, Title} from '@mantine/core';
|
import {Divider, Text, Title} from '@mantine/core';
|
||||||
import {Draggable, Droppable} from "@hello-pangea/dnd";
|
import {Draggable, Droppable} from "@hello-pangea/dnd";
|
||||||
import CreateDealButton from "../CreateDealButton/CreateDealButton.tsx";
|
import CreateDealButton from "../CreateDealButton/CreateDealButton.tsx";
|
||||||
|
import {DealSummary} from "../../../client";
|
||||||
|
import DealSummaryCard from "../DealSummaryCard/DealSummaryCard.tsx";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
droppableId: string;
|
droppableId: string;
|
||||||
title: string;
|
title: string;
|
||||||
withCreateButton?: boolean;
|
withCreateButton?: boolean;
|
||||||
|
summaries: DealSummary[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Board: FC<Props> = ({droppableId, title, withCreateButton = false}) => {
|
export const Board: FC<Props> = ({droppableId, title, summaries, withCreateButton = false}) => {
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -20,26 +24,43 @@ export const Board: FC<Props> = ({droppableId, title, withCreateButton = false})
|
|||||||
<Text>12 сделок: 500р</Text>
|
<Text>12 сделок: 500р</Text>
|
||||||
<Divider size={"xl"} my={10} color={"blue"}/>
|
<Divider size={"xl"} my={10} color={"blue"}/>
|
||||||
</div>
|
</div>
|
||||||
<Droppable droppableId={droppableId}>
|
<Droppable
|
||||||
{(provided) => (
|
droppableId={droppableId}
|
||||||
<div ref={provided.innerRef} className={styles["items-list"]}>
|
>
|
||||||
|
{(provided, snapshot) => (
|
||||||
|
<div ref={provided.innerRef}
|
||||||
|
className={classNames(
|
||||||
|
styles["items-list"],
|
||||||
|
(snapshot.isDraggingOver && !snapshot.draggingFromThisWith)
|
||||||
|
&& styles["items-list-drag-over"]
|
||||||
|
)}
|
||||||
|
{...provided.droppableProps}
|
||||||
|
>
|
||||||
{withCreateButton &&
|
{withCreateButton &&
|
||||||
<CreateDealButton
|
<CreateDealButton
|
||||||
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
}}
|
}}
|
||||||
/>}
|
/>}
|
||||||
<Draggable draggableId={droppableId + '1'} index={1}>
|
{summaries.map(summary =>
|
||||||
{(provided) => (
|
(
|
||||||
<div {...provided.draggableProps}
|
<Draggable
|
||||||
{...provided.dragHandleProps}
|
draggableId={summary.id.toString()}
|
||||||
ref={provided.innerRef}
|
index={1}
|
||||||
|
key={summary.id}
|
||||||
>
|
>
|
||||||
</div>
|
{(provided) => (
|
||||||
|
<div {...provided.draggableProps}
|
||||||
|
{...provided.dragHandleProps}
|
||||||
|
ref={provided.innerRef}
|
||||||
|
|
||||||
)}
|
>
|
||||||
</Draggable>
|
<DealSummaryCard dealSummary={summary}/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
)}
|
||||||
|
</Draggable>
|
||||||
|
))}
|
||||||
|
|
||||||
{provided.placeholder}
|
{provided.placeholder}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
min-height: 5rem;
|
min-height: 5rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: stretch;
|
||||||
|
text-align: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
border: dashed var(--item-border-size) var(--mantine-color-default-border);
|
border: dashed var(--item-border-size) var(--mantine-color-default-border);
|
||||||
border-radius: var(--item-border-radius);
|
border-radius: var(--item-border-radius);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {dateWithoutTimezone} from "../../../shared/lib/utils.ts";
|
|||||||
type Props = {
|
type Props = {
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
}
|
}
|
||||||
const CreateDealButton: FC<Props> = ({onClick}) => {
|
const CreateDealButton: FC<Props> = () => {
|
||||||
const [isCreating, setIsCreating] = useState(false);
|
const [isCreating, setIsCreating] = useState(false);
|
||||||
const [isTransitionEnded, setIsTransitionEnded] = useState(true);
|
const [isTransitionEnded, setIsTransitionEnded] = useState(true);
|
||||||
return (
|
return (
|
||||||
@@ -27,6 +27,7 @@ const CreateDealButton: FC<Props> = ({onClick}) => {
|
|||||||
mounted={isCreating}
|
mounted={isCreating}
|
||||||
transition={"scale-y"}
|
transition={"scale-y"}
|
||||||
onExited={() => setIsTransitionEnded(true)}
|
onExited={() => setIsTransitionEnded(true)}
|
||||||
|
|
||||||
>
|
>
|
||||||
{(styles) => (
|
{(styles) => (
|
||||||
<div style={styles}>
|
<div style={styles}>
|
||||||
@@ -35,7 +36,6 @@ const CreateDealButton: FC<Props> = ({onClick}) => {
|
|||||||
setIsCreating(false)
|
setIsCreating(false)
|
||||||
}}
|
}}
|
||||||
onSubmit={(quickDeal) => {
|
onSubmit={(quickDeal) => {
|
||||||
console.log(quickDeal);
|
|
||||||
DealService.quickCreateDealQuickCreatePost({
|
DealService.quickCreateDealQuickCreatePost({
|
||||||
requestBody: {
|
requestBody: {
|
||||||
...quickDeal,
|
...quickDeal,
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
.container {
|
||||||
|
min-height: 5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
border: dashed var(--item-border-size) var(--mantine-color-default-border);
|
||||||
|
border-radius: var(--item-border-radius);
|
||||||
|
cursor: pointer;
|
||||||
|
padding-left: rem(10);
|
||||||
|
padding-right: rem(10);
|
||||||
|
padding-top: rem(5);
|
||||||
|
padding-bottom: rem(5);
|
||||||
|
font-size: var(--mantine-font-size-sm);
|
||||||
|
flex: 1;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
@mixin light {
|
||||||
|
background-color: var(--mantine-color-gray-0);
|
||||||
|
}
|
||||||
|
@mixin dark {
|
||||||
|
background-color: var(--mantine-color-dark-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-row-right {
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
46
src/components/Dnd/DealSummaryCard/DealSummaryCard.tsx
Normal file
46
src/components/Dnd/DealSummaryCard/DealSummaryCard.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import {FC} from "react";
|
||||||
|
import {DealSummary} from "../../../client";
|
||||||
|
import styles from './DealSummaryCard.module.css';
|
||||||
|
|
||||||
|
import {Text} from '@mantine/core';
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
dealSummary: DealSummary
|
||||||
|
}
|
||||||
|
|
||||||
|
const DealSummaryCard: FC<Props> = ({dealSummary}) => {
|
||||||
|
return (
|
||||||
|
<div className={styles['container']}>
|
||||||
|
<div className={styles['flex-row']}>
|
||||||
|
<div className={styles['flex-item']}>
|
||||||
|
<Text size={"sm"} c={"gray.6"}>
|
||||||
|
{dealSummary.client_name}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
<div className={styles['flex-item']}>
|
||||||
|
<Text size={"md"} c={"blue.5"}>{dealSummary.name}</Text>
|
||||||
|
</div>
|
||||||
|
<div className={styles['flex-item']}>
|
||||||
|
<Text size={"sm"} c={"gray.6"}>
|
||||||
|
228 руб
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={classNames(styles['flex-row'], styles['flex-row-right'])}>
|
||||||
|
<div className={styles['flex-item']}>
|
||||||
|
<Text size={"sm"} c={"gray.6"}>
|
||||||
|
{new Date(dealSummary.changed_at).toLocaleString('ru-RU')}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
<div className={styles['flex-item']}>
|
||||||
|
<Text size={"sm"} c={"yellow.8"}>
|
||||||
|
Нет задач
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default DealSummaryCard;
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.header-button {
|
.header-button {
|
||||||
height: 100%;
|
height: 100% !important;
|
||||||
width: 10%;
|
width: 10%;
|
||||||
min-width: 5rem;
|
min-width: 10rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const Header: FC = ()=>{
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles['header']}>
|
<div className={styles['header']}>
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
radius={0}
|
radius={0}
|
||||||
placeholder={"Поиск и фильтры"}
|
placeholder={"Поиск и фильтры"}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {Center, Image, rem, Stack, Tooltip, UnstyledButton, useMantineColorScheme} from '@mantine/core';
|
import {Center, Image, rem, Stack, Tooltip, UnstyledButton, useMantineColorScheme} from '@mantine/core';
|
||||||
import {IconCash, IconHome2, IconLogout, IconMan, IconMoon, IconSun,} from '@tabler/icons-react';
|
import {IconBox, IconCash, IconHome2, IconLogout, IconMan, IconMoon, IconSun,} from '@tabler/icons-react';
|
||||||
import classes from './Navbar.module.css';
|
import classes from './Navbar.module.css';
|
||||||
import {useAppDispatch} from "../../redux/store.ts";
|
import {useAppDispatch} from "../../redux/store.ts";
|
||||||
import {logout} from "../../features/authSlice.ts";
|
import {logout} from "../../features/authSlice.ts";
|
||||||
@@ -44,7 +44,12 @@ const mockdata = [
|
|||||||
icon: IconMan,
|
icon: IconMan,
|
||||||
label: 'Клиенты',
|
label: 'Клиенты',
|
||||||
href: '/clients'
|
href: '/clients'
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
icon: IconBox,
|
||||||
|
label: 'Услуги',
|
||||||
|
href: '/services'
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function Navbar() {
|
export function Navbar() {
|
||||||
|
|||||||
6
src/components/PageBlock/PageBlock.module.css
Normal file
6
src/components/PageBlock/PageBlock.module.css
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
.container {
|
||||||
|
border-radius: rem(20);
|
||||||
|
background-color: var(--mantine-color-body);
|
||||||
|
padding: rem(10);
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
14
src/components/PageBlock/PageBlock.tsx
Normal file
14
src/components/PageBlock/PageBlock.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import {FC, ReactNode} from "react";
|
||||||
|
import styles from './PageBlock.module.css';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: ReactNode
|
||||||
|
}
|
||||||
|
export const PageBlock: FC<Props> = ({children}) => {
|
||||||
|
return (
|
||||||
|
<div className={styles['container']}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default PageBlock;
|
||||||
@@ -42,7 +42,7 @@ const ClientSelect: FC<Props> = ({onSelect, addressRestProps, nameRestProps, wit
|
|||||||
{
|
{
|
||||||
name: value,
|
name: value,
|
||||||
id: -1,
|
id: -1,
|
||||||
address: ''
|
address: ""
|
||||||
});
|
});
|
||||||
}, [value]);
|
}, [value]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -74,6 +74,12 @@ const ClientSelect: FC<Props> = ({onSelect, addressRestProps, nameRestProps, wit
|
|||||||
{withAddress &&
|
{withAddress &&
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder={'Клиент: адрес'}
|
placeholder={'Клиент: адрес'}
|
||||||
|
styles={{
|
||||||
|
input: {
|
||||||
|
borderTopLeftRadius: 0,
|
||||||
|
borderTopRightRadius: 0,
|
||||||
|
}
|
||||||
|
}}
|
||||||
value={selectedClient?.address || ''}
|
value={selectedClient?.address || ''}
|
||||||
onChange={event => {
|
onChange={event => {
|
||||||
selectClient(prevState => prevState && {...prevState, address: event.target.value})
|
selectClient(prevState => prevState && {...prevState, address: event.target.value})
|
||||||
|
|||||||
13
src/main.tsx
13
src/main.tsx
@@ -9,6 +9,8 @@ import {store} from "./redux/store.ts";
|
|||||||
import '@mantine/core/styles.css';
|
import '@mantine/core/styles.css';
|
||||||
import '@mantine/notifications/styles.css';
|
import '@mantine/notifications/styles.css';
|
||||||
import '@mantine/dates/styles.css';
|
import '@mantine/dates/styles.css';
|
||||||
|
import 'mantine-react-table/styles.css';
|
||||||
|
|
||||||
import 'dayjs/locale/ru';
|
import 'dayjs/locale/ru';
|
||||||
|
|
||||||
import './main.css';
|
import './main.css';
|
||||||
@@ -16,6 +18,7 @@ import {Notifications} from "@mantine/notifications";
|
|||||||
import {ModalsProvider} from "@mantine/modals";
|
import {ModalsProvider} from "@mantine/modals";
|
||||||
import {OpenAPI} from "./client";
|
import {OpenAPI} from "./client";
|
||||||
import {DatesProvider} from "@mantine/dates";
|
import {DatesProvider} from "@mantine/dates";
|
||||||
|
import {modals} from "./modals/modals.ts";
|
||||||
|
|
||||||
// Configuring router
|
// Configuring router
|
||||||
const router = createRouter({routeTree})
|
const router = createRouter({routeTree})
|
||||||
@@ -25,6 +28,12 @@ declare module '@tanstack/react-router' {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module '@mantine/modals' {
|
||||||
|
export interface MantineModalsOverride {
|
||||||
|
modals: typeof modals;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Configuring query
|
// Configuring query
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
@@ -36,13 +45,11 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
|
|||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<MantineProvider defaultColorScheme={"dark"}>
|
<MantineProvider defaultColorScheme={"dark"}>
|
||||||
<ModalsProvider>
|
<ModalsProvider modals={modals}>
|
||||||
<DatesProvider settings={{locale: 'ru'}}>
|
<DatesProvider settings={{locale: 'ru'}}>
|
||||||
|
|
||||||
<RouterProvider router={router}/>
|
<RouterProvider router={router}/>
|
||||||
<Notifications/>
|
<Notifications/>
|
||||||
</DatesProvider>
|
</DatesProvider>
|
||||||
|
|
||||||
</ModalsProvider>
|
</ModalsProvider>
|
||||||
</MantineProvider>
|
</MantineProvider>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
|
|||||||
19
src/modals/EnterDeadlineModal/EnterDeadlineModal.tsx
Normal file
19
src/modals/EnterDeadlineModal/EnterDeadlineModal.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import {ContextModalProps} from "@mantine/modals";
|
||||||
|
import {Button, Text} from "@mantine/core";
|
||||||
|
|
||||||
|
const EnterDeadlineModal = ({
|
||||||
|
context,
|
||||||
|
id,
|
||||||
|
innerProps,
|
||||||
|
}: ContextModalProps<{ modalBody: string }>) => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Text size="sm">{innerProps.modalBody}</Text>
|
||||||
|
<Button fullWidth mt="md" onClick={() => context.closeModal(id)}>
|
||||||
|
Close modal
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
export default EnterDeadlineModal;
|
||||||
9
src/modals/modals.ts
Normal file
9
src/modals/modals.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import EnterDeadlineModal from "./EnterDeadlineModal/EnterDeadlineModal.tsx";
|
||||||
|
import CreateServiceCategoryModal from "../pages/ServicesPage/modals/CreateServiceCategoryModal.tsx";
|
||||||
|
import CreateServiceModal from "../pages/ServicesPage/modals/CreateServiceModal.tsx";
|
||||||
|
|
||||||
|
export const modals = {
|
||||||
|
enterDeadline: EnterDeadlineModal,
|
||||||
|
createServiceCategory: CreateServiceCategoryModal,
|
||||||
|
createService: CreateServiceModal
|
||||||
|
}
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
import {FC} from "react";
|
import {FC} from "react";
|
||||||
import styles from './ClientsPage.module.css';
|
|
||||||
import ClientsTable from "./components/ClientsTable/ClientsTable.tsx";
|
import ClientsTable from "./components/ClientsTable/ClientsTable.tsx";
|
||||||
import {ClientService} from "../../client";
|
import useClientsList from "./hooks/useClientsList.tsx";
|
||||||
import {useQuery} from "@tanstack/react-query";
|
import PageBlock from "../../components/PageBlock/PageBlock.tsx";
|
||||||
|
|
||||||
|
|
||||||
const ClientsPage: FC = () => {
|
const ClientsPage: FC = () => {
|
||||||
const {data} = useQuery({queryKey: ['clients'], queryFn: ClientService.getAllClients})
|
const {clients} = useClientsList();
|
||||||
return (
|
return (
|
||||||
<div className={styles['container']}>
|
<>
|
||||||
<ClientsTable data={data?.clients || []}/>
|
<PageBlock>
|
||||||
</div>
|
|
||||||
|
<ClientsTable data={clients}/>
|
||||||
|
</PageBlock>
|
||||||
|
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ClientsPage;
|
export default ClientsPage;
|
||||||
@@ -1,52 +1,28 @@
|
|||||||
import {Checkbox, rem, Table} from "@mantine/core";
|
import {FC} from "react";
|
||||||
import {columns} from "./columns.tsx";
|
import {BaseTable} from "../../../../components/BaseTable/BaseTable.tsx";
|
||||||
import React, {FC} from "react";
|
import {useClientsTableColumns} from "./columns.tsx";
|
||||||
import {Client} from "../../../../types/Client.ts";
|
import {MantineReactTable, MRT_Table, MRT_TableOptions, useMantineReactTable} from "mantine-react-table";
|
||||||
import {IconEdit} from "@tabler/icons-react";
|
import {ClientSchema} from "../../../../client";
|
||||||
import {notifications} from "../../../../shared/lib/notifications.ts";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
data: Client[];
|
data: ClientSchema[];
|
||||||
|
|
||||||
}
|
}
|
||||||
const ClientsTable: FC<Props> = ({data}) => {
|
const ClientsTable: FC<Props> = ({data}) => {
|
||||||
|
const columns = useClientsTableColumns();
|
||||||
return (
|
return (
|
||||||
<Table.ScrollContainer minWidth={rem(50)}>
|
<BaseTable
|
||||||
<Table striped>
|
striped
|
||||||
|
data={data}
|
||||||
<Table.Thead>
|
columns={columns}
|
||||||
<Table.Tr>
|
restProps={{
|
||||||
<Table.Th/>
|
enableSorting: false,
|
||||||
|
enableColumnActions: false,
|
||||||
{columns.map(column => (
|
enableRowSelection: true,
|
||||||
<Table.Th>{column.header}</Table.Th>
|
enableColumnResizing: true,
|
||||||
))}
|
layoutMode: "grid"
|
||||||
</Table.Tr>
|
} as MRT_TableOptions<ClientSchema>}
|
||||||
</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>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,25 @@
|
|||||||
import {Client} from "../../../../types/Client.ts";
|
import {useMemo} from "react";
|
||||||
|
import {MRT_ColumnDef} from "mantine-react-table";
|
||||||
|
import {ClientSchema} from "../../../../client";
|
||||||
|
|
||||||
type Column = {
|
|
||||||
accessorKey: keyof Client;
|
export const useClientsTableColumns = () => {
|
||||||
header: string;
|
return useMemo<MRT_ColumnDef<ClientSchema>[]>(() => [
|
||||||
|
{
|
||||||
|
accessorKey: "name",
|
||||||
|
header: "Имя",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "details.address",
|
||||||
|
header: "Адрес"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "details.email",
|
||||||
|
header: "EMAIL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "details.phone_number",
|
||||||
|
header: "Телефон"
|
||||||
|
}
|
||||||
|
], []);
|
||||||
}
|
}
|
||||||
export const columns: Column[] = [
|
|
||||||
{
|
|
||||||
accessorKey: 'name',
|
|
||||||
header: 'Название'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
13
src/pages/ClientsPage/hooks/useClientsList.tsx
Normal file
13
src/pages/ClientsPage/hooks/useClientsList.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import {ClientService} from "../../../client";
|
||||||
|
import {useQuery} from "@tanstack/react-query";
|
||||||
|
|
||||||
|
const useClientsList = () => {
|
||||||
|
const {isPending, error, data, refetch} = useQuery({
|
||||||
|
queryKey: ['getAllClients'],
|
||||||
|
queryFn: ClientService.getAllClients
|
||||||
|
});
|
||||||
|
const clients = isPending || error || !data ? [] : data.clients;
|
||||||
|
|
||||||
|
return {clients, refetch}
|
||||||
|
}
|
||||||
|
export default useClientsList;
|
||||||
15
src/pages/LeadsPage/hooks/useDealSummaries.tsx
Normal file
15
src/pages/LeadsPage/hooks/useDealSummaries.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import {useQuery} from "@tanstack/react-query";
|
||||||
|
import {DealService, DealSummary} from "../../../client";
|
||||||
|
|
||||||
|
export const useDealSummaries = (): DealSummary[] => {
|
||||||
|
const {data: summaries = []} = useQuery({
|
||||||
|
queryKey: ['getDealSummaries'],
|
||||||
|
queryFn: DealService.getDealSummaries,
|
||||||
|
select: data => data.summaries || [] // Трансформируем полученные данные
|
||||||
|
});
|
||||||
|
|
||||||
|
// Теперь summaries будет содержать либо трансформированные данные, либо пустой массив по умолчанию
|
||||||
|
// isLoading и isError могут быть использованы для отображения индикаторов загрузки или ошибки
|
||||||
|
|
||||||
|
return summaries;
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +1,70 @@
|
|||||||
import {FC, useEffect} from "react";
|
import React, {FC, useEffect, useState} from "react";
|
||||||
import styles from './LeadsPage.module.css';
|
import styles from './LeadsPage.module.css';
|
||||||
import Board from "../../../components/Dnd/Board/Board.tsx";
|
import Board from "../../../components/Dnd/Board/Board.tsx";
|
||||||
import {Button, TextInput} from "@mantine/core";
|
|
||||||
import {DragDropContext} from "@hello-pangea/dnd";
|
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";
|
||||||
|
|
||||||
|
|
||||||
export const LeadsPage: FC = () => {
|
export const LeadsPage: FC = () => {
|
||||||
|
const summariesRaw = useDealSummaries();
|
||||||
|
const [summaries, setSummaries] = useState(summariesRaw);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// dispatch(setIsLoading(true));
|
setSummaries(summariesRaw);
|
||||||
}, []);
|
}, [summariesRaw]);
|
||||||
|
const onDragEnd = () => {
|
||||||
|
// if (!result.destination) return;
|
||||||
|
//
|
||||||
|
// const newStatus = getDealStatusByName(
|
||||||
|
// result.destination.droppableId
|
||||||
|
// );
|
||||||
|
// const summaryId = parseInt(result.draggableId);
|
||||||
|
//
|
||||||
|
// return;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles['container']}>
|
<PageBlock>
|
||||||
<div className={styles['boards']}>
|
<div className={styles['container']}>
|
||||||
<DragDropContext onDragEnd={() => {
|
<div className={styles['boards']}>
|
||||||
}}>
|
<DragDropContext onDragEnd={onDragEnd}>
|
||||||
<Board title={"Ожидает приемки"} withCreateButton droppableId={"AWAITING_ACCEPTANCE"}/>
|
<Board
|
||||||
<Board title={"Упаковка"} droppableId={"PACKAGING"}/>
|
withCreateButton
|
||||||
<Board title={"Ожидает отгрузки"} droppableId={"AWAITING_SHIPMENT"}/>
|
summaries={summaries
|
||||||
<Board title={"Ожидает оплаты"} droppableId={"AWAITING_PAYMENT"}/>
|
.filter(summary => summary.status == DealStatus.AWAITING_ACCEPTANCE)}
|
||||||
<Board title={"Завершена"} droppableId={"COMPLETED"}/>
|
title={"Ожидает приемки"}
|
||||||
|
droppableId={"AWAITING_ACCEPTANCE"}
|
||||||
</DragDropContext>
|
/>
|
||||||
|
<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>
|
||||||
</div>
|
</PageBlock>
|
||||||
|
|
||||||
</>
|
</>
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|||||||
14
src/pages/PageWrapper/PageWrapper.module.css
Normal file
14
src/pages/PageWrapper/PageWrapper.module.css
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
.main-container {
|
||||||
|
@mixin dark {
|
||||||
|
background-color: var(--mantine-color-dark-6);
|
||||||
|
}
|
||||||
|
@mixin light {
|
||||||
|
background-color: var(--mantine-color-gray-0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: rem(20);
|
||||||
|
display: flex;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
@@ -1,31 +1,35 @@
|
|||||||
import {FC, ReactNode} from "react";
|
import {FC, ReactNode} from "react";
|
||||||
import {Flex, rem} from "@mantine/core";
|
import {AppShell, rem} from "@mantine/core";
|
||||||
import {Navbar} from "../../components/Navbar/Navbar.tsx";
|
import {Navbar} from "../../components/Navbar/Navbar.tsx";
|
||||||
import {useSelector} from "react-redux";
|
import {useSelector} from "react-redux";
|
||||||
import {RootState} from "../../redux/store.ts";
|
import {RootState} from "../../redux/store.ts";
|
||||||
import Header from "../../components/Header/Header.tsx";
|
import styles from './PageWrapper.module.css';
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
const PageWrapper: FC<Props> = ({children}) => {
|
const PageWrapper: FC<Props> = ({children}) => {
|
||||||
const authState = useSelector((state: RootState) => state.auth);
|
const authState = useSelector((state: RootState) => state.auth);
|
||||||
return (<Flex style={{flex: 1}}>
|
return (
|
||||||
|
<AppShell
|
||||||
{authState.isAuthorized &&
|
layout={"alt"}
|
||||||
|
navbar={{width: rem('80px'), breakpoint: "sm"}}
|
||||||
|
// header={{height:rem(60)}}
|
||||||
|
>
|
||||||
|
{/*<AppShell.Header>*/}
|
||||||
|
{/* <Header/>*/}
|
||||||
|
{/*</AppShell.Header>*/}
|
||||||
|
<AppShell.Navbar>
|
||||||
<Navbar/>
|
<Navbar/>
|
||||||
}
|
</AppShell.Navbar>
|
||||||
<div style={{flex: 1, display: 'flex', flexDirection: 'column'}}>
|
<AppShell.Main className={styles['main-container']}>
|
||||||
<Header/>
|
<div className={styles['container']}>
|
||||||
<div style={{padding:rem(20), flex:1}}>
|
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</Flex>
|
</AppShell.Main>
|
||||||
|
</AppShell>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export default PageWrapper;
|
export default PageWrapper;
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import {Select} from "@mantine/core";
|
||||||
|
import useServiceCategoriesList from "../../hooks/useServiceCategoriesList.tsx";
|
||||||
|
import {ServiceCategorySchema} from "../../../../client";
|
||||||
|
import {FC, ReactNode} from "react";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
fullWidth?: boolean,
|
||||||
|
defaultValue?: ServiceCategorySchema
|
||||||
|
onChange: (category: ServiceCategorySchema) => void
|
||||||
|
error?: ReactNode
|
||||||
|
}
|
||||||
|
const ServiceCategorySelect: FC<Props> = ({defaultValue, onChange, fullWidth, error}) => {
|
||||||
|
const {categories} = useServiceCategoriesList();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
error={error}
|
||||||
|
w={fullWidth ? "100%" : undefined}
|
||||||
|
checkIconPosition={"right"}
|
||||||
|
label={"Категория услуги"}
|
||||||
|
placeholder={"Выберите категорию услуги"}
|
||||||
|
data={categories.map(category => ({
|
||||||
|
label: category.name,
|
||||||
|
value: category.id.toString()
|
||||||
|
}))}
|
||||||
|
onChange={event => {
|
||||||
|
if (!event) return;
|
||||||
|
const category = categories.find(category => category.id == parseInt(event))
|
||||||
|
if (!category) return;
|
||||||
|
onChange(category)
|
||||||
|
}}
|
||||||
|
value={defaultValue && defaultValue.id.toString()}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ServiceCategorySelect;
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import {ServiceSchema} from "../../../../client";
|
||||||
|
import {FC, RefObject} from "react";
|
||||||
|
import {useServicesTableColumns} from "./columns.tsx";
|
||||||
|
import {BaseTable, BaseTableRef} from "../../../../components/BaseTable/BaseTable.tsx";
|
||||||
|
import {MRT_TableOptions} from "mantine-react-table";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
services: ServiceSchema[];
|
||||||
|
tableRef?: RefObject<BaseTableRef<ServiceSchema>>
|
||||||
|
}
|
||||||
|
const ServicesTable: FC<Props> = ({services, tableRef}) => {
|
||||||
|
const columns = useServicesTableColumns();
|
||||||
|
return (
|
||||||
|
<BaseTable
|
||||||
|
ref={tableRef}
|
||||||
|
data={services}
|
||||||
|
columns={columns}
|
||||||
|
restProps={{
|
||||||
|
enableGrouping: true,
|
||||||
|
initialState: {grouping: ["category"]},
|
||||||
|
enableColumnActions: false,
|
||||||
|
mantineCreateRowModalProps: {
|
||||||
|
transitionProps: {transition: 'rotate-left', duration: 300}
|
||||||
|
},
|
||||||
|
} as MRT_TableOptions<ServiceSchema>}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ServicesTable;
|
||||||
31
src/pages/ServicesPage/components/ServicesTable/columns.tsx
Normal file
31
src/pages/ServicesPage/components/ServicesTable/columns.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import {useMemo} from "react";
|
||||||
|
import {MRT_ColumnDef} from "mantine-react-table";
|
||||||
|
import {ServiceSchema} from "../../../../client";
|
||||||
|
|
||||||
|
export const useServicesTableColumns = () => {
|
||||||
|
return useMemo<MRT_ColumnDef<ServiceSchema>[]>(() => [
|
||||||
|
{
|
||||||
|
accessorKey: "category",
|
||||||
|
header: "Категория",
|
||||||
|
enableGrouping: false,
|
||||||
|
enableSorting: false,
|
||||||
|
accessorFn: (row) => row.category.name
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "name",
|
||||||
|
header: "Услуга",
|
||||||
|
enableGrouping: false,
|
||||||
|
enableSorting: false,
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "price",
|
||||||
|
header: "Цена",
|
||||||
|
enableGrouping: false,
|
||||||
|
enableSorting: false,
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
], []);
|
||||||
|
|
||||||
|
}
|
||||||
14
src/pages/ServicesPage/hooks/useServiceCategoriesList.tsx
Normal file
14
src/pages/ServicesPage/hooks/useServiceCategoriesList.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import {useQuery} from "@tanstack/react-query";
|
||||||
|
import {ServiceService} from "../../../client";
|
||||||
|
|
||||||
|
const useServiceCategoriesList = () => {
|
||||||
|
const {isPending, error, data, refetch} = useQuery({
|
||||||
|
queryKey: ['getAllServiceCategories'],
|
||||||
|
queryFn: ServiceService.getAllServiceCategories,
|
||||||
|
});
|
||||||
|
const categories = isPending || error || !data ? [] : data.categories;
|
||||||
|
|
||||||
|
return {categories, refetch}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useServiceCategoriesList;
|
||||||
13
src/pages/ServicesPage/hooks/useServicesList.tsx
Normal file
13
src/pages/ServicesPage/hooks/useServicesList.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import {useQuery} from "@tanstack/react-query";
|
||||||
|
import {ServiceService} from "../../../client";
|
||||||
|
|
||||||
|
const useServicesList = () => {
|
||||||
|
const {isPending, error, data, refetch} = useQuery({
|
||||||
|
queryKey: ['getAllServices'],
|
||||||
|
queryFn: ServiceService.getAllServices
|
||||||
|
});
|
||||||
|
const services = isPending || error || !data ? [] : data.services;
|
||||||
|
|
||||||
|
return {services, refetch}
|
||||||
|
}
|
||||||
|
export default useServicesList;
|
||||||
1
src/pages/ServicesPage/index.ts
Normal file
1
src/pages/ServicesPage/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export {ServicesPage} from './ui/ServicesPage.tsx';
|
||||||
52
src/pages/ServicesPage/modals/CreateServiceCategoryModal.tsx
Normal file
52
src/pages/ServicesPage/modals/CreateServiceCategoryModal.tsx
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import {ServiceCategorySchema} from "../../../client";
|
||||||
|
import {Button, Flex, rem, TextInput} from "@mantine/core";
|
||||||
|
import {useForm} from "@mantine/form";
|
||||||
|
import {ContextModalProps} from "@mantine/modals";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onCreate: (category: ServiceCategorySchema) => void
|
||||||
|
}
|
||||||
|
const CreateServiceCategoryModal = ({
|
||||||
|
context,
|
||||||
|
id,
|
||||||
|
innerProps,
|
||||||
|
}: ContextModalProps<Props>) => {
|
||||||
|
const form = useForm({
|
||||||
|
initialValues: {
|
||||||
|
name: ''
|
||||||
|
},
|
||||||
|
validate: {
|
||||||
|
name: (name) => name.trim() !== '' ? null : "Необходимо ввести название категории",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const onSubmit = (values: { name: string }) => {
|
||||||
|
innerProps.onCreate({name: values.name, id: -1});
|
||||||
|
context.closeContextModal(id);
|
||||||
|
|
||||||
|
}
|
||||||
|
const onCancelClick = () => {
|
||||||
|
context.closeContextModal(id);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<form onSubmit={form.onSubmit((values) => onSubmit(values))}>
|
||||||
|
<Flex gap={rem(10)} direction={"column"}>
|
||||||
|
<TextInput
|
||||||
|
|
||||||
|
placeholder={"Введите название категори"}
|
||||||
|
label={"Название категории"}
|
||||||
|
{...form.getInputProps('name')}
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
<Flex justify={"center"} mt={rem(5)} gap={rem(10)}>
|
||||||
|
<Button onClick={() => onCancelClick()} variant={"subtle"}>Отменить</Button>
|
||||||
|
<Button type={"submit"} variant={"default"}>Сохранить</Button>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default CreateServiceCategoryModal;
|
||||||
76
src/pages/ServicesPage/modals/CreateServiceModal.tsx
Normal file
76
src/pages/ServicesPage/modals/CreateServiceModal.tsx
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import {ServiceSchema} from "../../../client";
|
||||||
|
import {Button, Flex, NumberInput, rem, TextInput} from "@mantine/core";
|
||||||
|
import ServiceCategorySelect from "../components/ServiceCategorySelect/ServiceCategorySelect.tsx";
|
||||||
|
import {useForm} from "@mantine/form";
|
||||||
|
import {ContextModalProps} from "@mantine/modals";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onCreate: (service: ServiceSchema) => void
|
||||||
|
}
|
||||||
|
const CreateServiceModal = ({
|
||||||
|
context,
|
||||||
|
id,
|
||||||
|
innerProps,
|
||||||
|
}: ContextModalProps<Props>) => {
|
||||||
|
const form = useForm({
|
||||||
|
initialValues: {
|
||||||
|
category: {
|
||||||
|
id: -1,
|
||||||
|
name: ''
|
||||||
|
},
|
||||||
|
name: '',
|
||||||
|
price: NaN
|
||||||
|
},
|
||||||
|
validate: {
|
||||||
|
category: (category) => category.id >= 0 ? null : "Необходимо выбрать категорию",
|
||||||
|
name: (name) => name.trim() !== '' ? null : "Необходимо ввести название услуги",
|
||||||
|
price: (price) => !isNaN(price) ? null : "Небходимо ввести стоимость услуги"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const onSubmit = (values: { category: { id: number; name: string; }; name: string; price: number; }) => {
|
||||||
|
innerProps.onCreate({...values, id: -1});
|
||||||
|
context.closeContextModal(id);
|
||||||
|
}
|
||||||
|
const onCancelClick = () => {
|
||||||
|
context.closeContextModal(id);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<form onSubmit={form.onSubmit((values) => onSubmit(values))}>
|
||||||
|
<Flex gap={rem(10)} direction={"column"}>
|
||||||
|
|
||||||
|
<ServiceCategorySelect
|
||||||
|
fullWidth
|
||||||
|
onChange={event => {
|
||||||
|
form.setFieldValue("category", event)
|
||||||
|
}}
|
||||||
|
error={form.getInputProps("category").error}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
|
||||||
|
placeholder={"Введите название услуги"}
|
||||||
|
label={"Название услуги"}
|
||||||
|
{...form.getInputProps('name')}
|
||||||
|
/>
|
||||||
|
<NumberInput
|
||||||
|
placeholder={"Введите стоимость услуги"}
|
||||||
|
label={"Стоимость услуги"}
|
||||||
|
hideControls
|
||||||
|
decimalScale={2}
|
||||||
|
{...form.getInputProps('price')}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Flex justify={"center"} mt={rem(5)} gap={rem(10)}>
|
||||||
|
<Button onClick={() => onCancelClick()} variant={"subtle"}>Отменить</Button>
|
||||||
|
<Button type={"submit"} variant={"default"}>Сохранить</Button>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateServiceModal;
|
||||||
12
src/pages/ServicesPage/ui/ServicesPage.module.css
Normal file
12
src/pages/ServicesPage/ui/ServicesPage.module.css
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
gap: rem(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-panel {
|
||||||
|
padding: rem(5);
|
||||||
|
gap: rem(10);
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
67
src/pages/ServicesPage/ui/ServicesPage.tsx
Normal file
67
src/pages/ServicesPage/ui/ServicesPage.tsx
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import {FC, useRef} from "react";
|
||||||
|
import ServicesTable from "../components/ServicesTable/ServicesTable.tsx";
|
||||||
|
import useServicesList from "../hooks/useServicesList.tsx";
|
||||||
|
import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
|
||||||
|
import styles from './ServicesPage.module.css';
|
||||||
|
import {Button} from "@mantine/core";
|
||||||
|
import {BaseTableRef} from "../../../components/BaseTable/BaseTable.tsx";
|
||||||
|
import {ServiceCategorySchema, ServiceSchema, ServiceService} from "../../../client";
|
||||||
|
import {notifications} from "../../../shared/lib/notifications.ts";
|
||||||
|
import {modals} from "@mantine/modals";
|
||||||
|
|
||||||
|
export const ServicesPage: FC = () => {
|
||||||
|
const {services, refetch} = useServicesList();
|
||||||
|
|
||||||
|
const tableRef = useRef<BaseTableRef<ServiceSchema>>(null);
|
||||||
|
|
||||||
|
const onCreateClick = () => {
|
||||||
|
modals.openContextModal({
|
||||||
|
modal: 'createService',
|
||||||
|
title: 'Создание услуги',
|
||||||
|
withCloseButton: false,
|
||||||
|
innerProps: {
|
||||||
|
onCreate
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const onCreate = (values: ServiceSchema) => {
|
||||||
|
ServiceService.createService({requestBody: {service: values}})
|
||||||
|
.then(({ok, message}) => {
|
||||||
|
notifications.guess(ok, {message: message});
|
||||||
|
if (!ok) return;
|
||||||
|
refetch();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const onCreateCategoryClick = () => {
|
||||||
|
modals.openContextModal({
|
||||||
|
modal: "createServiceCategory",
|
||||||
|
title: 'Создание категории',
|
||||||
|
withCloseButton: false,
|
||||||
|
innerProps: {
|
||||||
|
onCreate: onCategoryCreate
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const onCategoryCreate = (category: ServiceCategorySchema) => {
|
||||||
|
ServiceService.createServiceCategory({requestBody: {category: category}})
|
||||||
|
.then(({ok, message}) =>
|
||||||
|
notifications.guess(ok, {message: message}))
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles['container']}>
|
||||||
|
<PageBlock>
|
||||||
|
<div className={styles['top-panel']}>
|
||||||
|
<Button onClick={onCreateClick} variant={"default"}>Создать услугу</Button>
|
||||||
|
<Button onClick={onCreateCategoryClick} variant={"default"}>Создать категорию</Button>
|
||||||
|
</div>
|
||||||
|
</PageBlock>
|
||||||
|
<PageBlock>
|
||||||
|
<ServicesTable
|
||||||
|
tableRef={tableRef}
|
||||||
|
services={services}
|
||||||
|
/>
|
||||||
|
</PageBlock>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ import { Route as rootRoute } from './routes/__root'
|
|||||||
// Create Virtual Routes
|
// Create Virtual Routes
|
||||||
|
|
||||||
const TestLazyImport = createFileRoute('/test')()
|
const TestLazyImport = createFileRoute('/test')()
|
||||||
|
const ServicesLazyImport = createFileRoute('/services')()
|
||||||
const LoginLazyImport = createFileRoute('/login')()
|
const LoginLazyImport = createFileRoute('/login')()
|
||||||
const LeadsLazyImport = createFileRoute('/leads')()
|
const LeadsLazyImport = createFileRoute('/leads')()
|
||||||
const ClientsLazyImport = createFileRoute('/clients')()
|
const ClientsLazyImport = createFileRoute('/clients')()
|
||||||
@@ -29,6 +30,11 @@ const TestLazyRoute = TestLazyImport.update({
|
|||||||
getParentRoute: () => rootRoute,
|
getParentRoute: () => rootRoute,
|
||||||
} as any).lazy(() => import('./routes/test.lazy').then((d) => d.Route))
|
} as any).lazy(() => import('./routes/test.lazy').then((d) => d.Route))
|
||||||
|
|
||||||
|
const ServicesLazyRoute = ServicesLazyImport.update({
|
||||||
|
path: '/services',
|
||||||
|
getParentRoute: () => rootRoute,
|
||||||
|
} as any).lazy(() => import('./routes/services.lazy').then((d) => d.Route))
|
||||||
|
|
||||||
const LoginLazyRoute = LoginLazyImport.update({
|
const LoginLazyRoute = LoginLazyImport.update({
|
||||||
path: '/login',
|
path: '/login',
|
||||||
getParentRoute: () => rootRoute,
|
getParentRoute: () => rootRoute,
|
||||||
@@ -69,6 +75,10 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof LoginLazyImport
|
preLoaderRoute: typeof LoginLazyImport
|
||||||
parentRoute: typeof rootRoute
|
parentRoute: typeof rootRoute
|
||||||
}
|
}
|
||||||
|
'/services': {
|
||||||
|
preLoaderRoute: typeof ServicesLazyImport
|
||||||
|
parentRoute: typeof rootRoute
|
||||||
|
}
|
||||||
'/test': {
|
'/test': {
|
||||||
preLoaderRoute: typeof TestLazyImport
|
preLoaderRoute: typeof TestLazyImport
|
||||||
parentRoute: typeof rootRoute
|
parentRoute: typeof rootRoute
|
||||||
@@ -83,6 +93,7 @@ export const routeTree = rootRoute.addChildren([
|
|||||||
ClientsLazyRoute,
|
ClientsLazyRoute,
|
||||||
LeadsLazyRoute,
|
LeadsLazyRoute,
|
||||||
LoginLazyRoute,
|
LoginLazyRoute,
|
||||||
|
ServicesLazyRoute,
|
||||||
TestLazyRoute,
|
TestLazyRoute,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|||||||
6
src/routes/services.lazy.tsx
Normal file
6
src/routes/services.lazy.tsx
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import {createLazyFileRoute} from "@tanstack/react-router";
|
||||||
|
import {ServicesPage} from "../pages/ServicesPage";
|
||||||
|
|
||||||
|
export const Route = createLazyFileRoute('/services')({
|
||||||
|
component: ServicesPage
|
||||||
|
})
|
||||||
13
src/shared/enums/DealStatus.ts
Normal file
13
src/shared/enums/DealStatus.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
export enum DealStatus {
|
||||||
|
CREATED = 0,
|
||||||
|
AWAITING_ACCEPTANCE = 1,
|
||||||
|
PACKAGING = 2,
|
||||||
|
AWAITING_SHIPMENT = 3,
|
||||||
|
AWAITING_PAYMENT = 4,
|
||||||
|
COMPLETED = 5,
|
||||||
|
CANCELLED = 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getDealStatusByName = (name: string): DealStatus => {
|
||||||
|
return DealStatus[name as keyof typeof DealStatus];
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
export type Client = {
|
export type Client = {
|
||||||
id?: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
address: string;
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,8 @@
|
|||||||
"lib": [
|
"lib": [
|
||||||
"ES2020",
|
"ES2020",
|
||||||
"DOM",
|
"DOM",
|
||||||
"DOM.Iterable"
|
"DOM.Iterable",
|
||||||
|
"ESNext"
|
||||||
],
|
],
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
@@ -20,7 +21,7 @@
|
|||||||
"strict": true,
|
"strict": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": false,
|
||||||
"allowSyntheticDefaultImports": true
|
"allowSyntheticDefaultImports": true
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
|
|||||||
Reference in New Issue
Block a user