crap
This commit is contained in:
@@ -13,14 +13,21 @@ export type { ClientDetailsSchema } from './models/ClientDetailsSchema';
|
|||||||
export type { ClientGetAllResponse } from './models/ClientGetAllResponse';
|
export type { ClientGetAllResponse } from './models/ClientGetAllResponse';
|
||||||
export type { ClientSchema } from './models/ClientSchema';
|
export type { ClientSchema } from './models/ClientSchema';
|
||||||
export type { ClientUpdateDetailsRequest } from './models/ClientUpdateDetailsRequest';
|
export type { ClientUpdateDetailsRequest } from './models/ClientUpdateDetailsRequest';
|
||||||
|
export type { DealAddServicesRequest } from './models/DealAddServicesRequest';
|
||||||
|
export type { DealAddServicesResponse } from './models/DealAddServicesResponse';
|
||||||
export type { DealChangeStatusRequest } from './models/DealChangeStatusRequest';
|
export type { DealChangeStatusRequest } from './models/DealChangeStatusRequest';
|
||||||
export type { DealChangeStatusResponse } from './models/DealChangeStatusResponse';
|
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 { DealServiceSchema } from './models/DealServiceSchema';
|
||||||
export type { DealSummary } from './models/DealSummary';
|
export type { DealSummary } from './models/DealSummary';
|
||||||
export type { DealSummaryResponse } from './models/DealSummaryResponse';
|
export type { DealSummaryResponse } from './models/DealSummaryResponse';
|
||||||
export type { HTTPValidationError } from './models/HTTPValidationError';
|
export type { HTTPValidationError } from './models/HTTPValidationError';
|
||||||
|
export type { ProductCreateRequest } from './models/ProductCreateRequest';
|
||||||
|
export type { ProductCreateResponse } from './models/ProductCreateResponse';
|
||||||
|
export type { ProductGetResponse } from './models/ProductGetResponse';
|
||||||
|
export type { ProductSchema } from './models/ProductSchema';
|
||||||
export type { ServiceCategorySchema } from './models/ServiceCategorySchema';
|
export type { ServiceCategorySchema } from './models/ServiceCategorySchema';
|
||||||
export type { ServiceCreateCategoryRequest } from './models/ServiceCreateCategoryRequest';
|
export type { ServiceCreateCategoryRequest } from './models/ServiceCreateCategoryRequest';
|
||||||
export type { ServiceCreateCategoryResponse } from './models/ServiceCreateCategoryResponse';
|
export type { ServiceCreateCategoryResponse } from './models/ServiceCreateCategoryResponse';
|
||||||
@@ -34,4 +41,5 @@ 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 { ProductService } from './services/ProductService';
|
||||||
export { ServiceService } from './services/ServiceService';
|
export { ServiceService } from './services/ServiceService';
|
||||||
|
|||||||
10
src/client/models/DealAddServicesRequest.ts
Normal file
10
src/client/models/DealAddServicesRequest.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { DealServiceSchema } from './DealServiceSchema';
|
||||||
|
export type DealAddServicesRequest = {
|
||||||
|
deal_id: number;
|
||||||
|
services: Array<DealServiceSchema>;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/DealAddServicesResponse.ts
Normal file
9
src/client/models/DealAddServicesResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type DealAddServicesResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/DealServiceSchema.ts
Normal file
9
src/client/models/DealServiceSchema.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type DealServiceSchema = {
|
||||||
|
id: number;
|
||||||
|
quantity: number;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -8,5 +8,6 @@ export type DealSummary = {
|
|||||||
client_name: string;
|
client_name: string;
|
||||||
changed_at: string;
|
changed_at: string;
|
||||||
status: number;
|
status: number;
|
||||||
|
total_price: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
10
src/client/models/ProductCreateRequest.ts
Normal file
10
src/client/models/ProductCreateRequest.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type ProductCreateRequest = {
|
||||||
|
name: string;
|
||||||
|
article: string;
|
||||||
|
client_id: number;
|
||||||
|
};
|
||||||
|
|
||||||
8
src/client/models/ProductCreateResponse.ts
Normal file
8
src/client/models/ProductCreateResponse.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type ProductCreateResponse = {
|
||||||
|
product_id: number;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/ProductGetResponse.ts
Normal file
9
src/client/models/ProductGetResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { ProductSchema } from './ProductSchema';
|
||||||
|
export type ProductGetResponse = {
|
||||||
|
products: Array<ProductSchema>;
|
||||||
|
};
|
||||||
|
|
||||||
11
src/client/models/ProductSchema.ts
Normal file
11
src/client/models/ProductSchema.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type ProductSchema = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
article: string;
|
||||||
|
client_id: number;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
/* istanbul ignore file */
|
/* istanbul ignore file */
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
import type { DealAddServicesRequest } from '../models/DealAddServicesRequest';
|
||||||
|
import type { DealAddServicesResponse } from '../models/DealAddServicesResponse';
|
||||||
import type { DealChangeStatusRequest } from '../models/DealChangeStatusRequest';
|
import type { DealChangeStatusRequest } from '../models/DealChangeStatusRequest';
|
||||||
import type { DealChangeStatusResponse } from '../models/DealChangeStatusResponse';
|
import type { DealChangeStatusResponse } from '../models/DealChangeStatusResponse';
|
||||||
import type { DealCreateRequest } from '../models/DealCreateRequest';
|
import type { DealCreateRequest } from '../models/DealCreateRequest';
|
||||||
@@ -83,4 +85,24 @@ export class DealService {
|
|||||||
url: '/deal/summaries',
|
url: '/deal/summaries',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Services Add
|
||||||
|
* @returns DealAddServicesResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static servicesAddDealServicesAddPost({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: DealAddServicesRequest,
|
||||||
|
}): CancelablePromise<DealAddServicesResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/deal/services/add',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
59
src/client/services/ProductService.ts
Normal file
59
src/client/services/ProductService.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { ProductCreateRequest } from '../models/ProductCreateRequest';
|
||||||
|
import type { ProductCreateResponse } from '../models/ProductCreateResponse';
|
||||||
|
import type { ProductGetResponse } from '../models/ProductGetResponse';
|
||||||
|
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||||
|
import { OpenAPI } from '../core/OpenAPI';
|
||||||
|
import { request as __request } from '../core/request';
|
||||||
|
export class ProductService {
|
||||||
|
/**
|
||||||
|
* Create Product
|
||||||
|
* @returns ProductCreateResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static createProductProductCreatePost({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: ProductCreateRequest,
|
||||||
|
}): CancelablePromise<ProductCreateResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/product/create',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get Product
|
||||||
|
* @returns ProductGetResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static getProductsByClientId({
|
||||||
|
clientId,
|
||||||
|
page,
|
||||||
|
itemsPerPage,
|
||||||
|
}: {
|
||||||
|
clientId: number,
|
||||||
|
page: number,
|
||||||
|
itemsPerPage: number,
|
||||||
|
}): CancelablePromise<ProductGetResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/product/get',
|
||||||
|
query: {
|
||||||
|
'client_id': clientId,
|
||||||
|
'page': page,
|
||||||
|
'items_per_page': itemsPerPage,
|
||||||
|
},
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ import {QuickDeal} from "../../../types/QuickDeal.ts";
|
|||||||
import {FC} from "react";
|
import {FC} from "react";
|
||||||
import {useForm} from "@mantine/form";
|
import {useForm} from "@mantine/form";
|
||||||
import styles from './CreateDealForm.module.css';
|
import styles from './CreateDealForm.module.css';
|
||||||
import ClientSelect from "../../Selects/ClientSelect/ClientSelect.tsx";
|
import ClientAutocomplete from "../../Selects/ClientAutocomplete/ClientAutocomplete.tsx";
|
||||||
import {DateTimePicker} from "@mantine/dates";
|
import {DateTimePicker} from "@mantine/dates";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -41,7 +41,7 @@ const CreateDealFrom: FC<Props> = ({onSubmit, onCancel}) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles['inputs']}>
|
<div className={styles['inputs']}>
|
||||||
<ClientSelect
|
<ClientAutocomplete
|
||||||
withAddress
|
withAddress
|
||||||
nameRestProps={form.getInputProps('client_name')}
|
nameRestProps={form.getInputProps('client_name')}
|
||||||
addressRestProps={form.getInputProps('client_address')}
|
addressRestProps={form.getInputProps('client_address')}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const DealSummaryCard: FC<Props> = ({dealSummary}) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className={styles['flex-item']}>
|
<div className={styles['flex-item']}>
|
||||||
<Text size={"sm"} c={"gray.6"}>
|
<Text size={"sm"} c={"gray.6"}>
|
||||||
228 руб
|
{dealSummary.total_price.toLocaleString('ru-RU')} руб
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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 {IconBox, IconCash, IconHome2, IconLogout, IconMan, IconMoon, IconSun,} from '@tabler/icons-react';
|
import {IconBarcode, 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";
|
||||||
@@ -50,6 +50,11 @@ const mockdata = [
|
|||||||
label: 'Услуги',
|
label: 'Услуги',
|
||||||
href: '/services'
|
href: '/services'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: IconBarcode,
|
||||||
|
label: 'Товары',
|
||||||
|
href: '/products'
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
export function Navbar() {
|
export function Navbar() {
|
||||||
|
|||||||
@@ -2,5 +2,8 @@
|
|||||||
border-radius: rem(20);
|
border-radius: rem(20);
|
||||||
background-color: var(--mantine-color-body);
|
background-color: var(--mantine-color-body);
|
||||||
padding: rem(10);
|
padding: rem(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-fluid {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
import {FC, ReactNode} from "react";
|
import {FC, ReactNode} from "react";
|
||||||
import styles from './PageBlock.module.css';
|
import styles from './PageBlock.module.css';
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
|
fluid?: boolean;
|
||||||
}
|
}
|
||||||
export const PageBlock: FC<Props> = ({children}) => {
|
export const PageBlock: FC<Props> = ({children, fluid = true}) => {
|
||||||
return (
|
return (
|
||||||
<div className={styles['container']}>
|
<div className={classNames(styles['container'], fluid && styles['container-fluid'])}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
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 ClientAutocomplete: 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={'Клиент: адрес'}
|
||||||
|
styles={{
|
||||||
|
input: {
|
||||||
|
borderTopLeftRadius: 0,
|
||||||
|
borderTopRightRadius: 0,
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
value={selectedClient?.address || ''}
|
||||||
|
onChange={event => {
|
||||||
|
selectClient(prevState => prevState && {...prevState, address: event.target.value})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default ClientAutocomplete;
|
||||||
@@ -1,93 +1,31 @@
|
|||||||
import {useDebouncedValue} from "@mantine/hooks";
|
import {FC} from "react";
|
||||||
import {Autocomplete, AutocompleteProps, TextInput, TextInputProps} from "@mantine/core";
|
import {Select} from "@mantine/core";
|
||||||
import {FC, useEffect, useState} from "react";
|
import {ClientSchema} from "../../../client";
|
||||||
import {Client} from "../../../types/Client.ts";
|
import useClientsList from "../../../pages/ClientsPage/hooks/useClientsList.tsx";
|
||||||
import {ClientService} from "../../../client";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onSelect?: (client: Client) => void;
|
value?: ClientSchema;
|
||||||
withAddress?: boolean;
|
onChange: (client: ClientSchema) => void;
|
||||||
nameRestProps?: AutocompleteProps;
|
withLabel?: boolean;
|
||||||
addressRestProps?: TextInputProps;
|
|
||||||
}
|
}
|
||||||
const ClientSelect: FC<Props> = ({onSelect, addressRestProps, nameRestProps, withAddress = false}) => {
|
const ClientSelect: FC<Props> = ({value, onChange, withLabel = false}) => {
|
||||||
const [value, setValue] = useState('');
|
const {clients} = useClientsList();
|
||||||
const [debouncedValue] = useDebouncedValue(value, 200);
|
const options = clients.map(client => ({label: client.name, value: client.id.toString()}))
|
||||||
|
|
||||||
// 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 (
|
return (
|
||||||
<>
|
<Select
|
||||||
<Autocomplete
|
|
||||||
{...nameRestProps}
|
|
||||||
placeholder={'Клиент: название'}
|
|
||||||
onChange={handleChange}
|
|
||||||
value={value}
|
|
||||||
data={clients.map(client => client.name)}
|
|
||||||
styles={withAddress ? {
|
|
||||||
input: {
|
|
||||||
borderBottomLeftRadius: 0,
|
|
||||||
borderBottomRightRadius: 0
|
|
||||||
}
|
|
||||||
} : {}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{withAddress &&
|
placeholder={"Выберите клиента"}
|
||||||
<TextInput
|
value={value && options.find(client => client.value == value.id.toString())?.value}
|
||||||
placeholder={'Клиент: адрес'}
|
|
||||||
styles={{
|
|
||||||
input: {
|
|
||||||
borderTopLeftRadius: 0,
|
|
||||||
borderTopRightRadius: 0,
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
value={selectedClient?.address || ''}
|
|
||||||
onChange={event => {
|
onChange={event => {
|
||||||
selectClient(prevState => prevState && {...prevState, address: event.target.value})
|
if (!event) return;
|
||||||
|
const client = clients.find(client => client.id == parseInt(event));
|
||||||
|
if (!client) return;
|
||||||
|
onChange(client);
|
||||||
}}
|
}}
|
||||||
|
data={options}
|
||||||
|
label={withLabel && "Клиент"}
|
||||||
/>
|
/>
|
||||||
}
|
|
||||||
</>
|
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export default ClientSelect;
|
export default ClientSelect;
|
||||||
@@ -10,5 +10,5 @@
|
|||||||
.container {
|
.container {
|
||||||
padding: rem(20);
|
padding: rem(20);
|
||||||
display: flex;
|
display: flex;
|
||||||
min-height: 100vh;
|
//min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import {ProductSchema} from "../../../../client";
|
||||||
|
import {FC, RefObject} from "react";
|
||||||
|
import {BaseTable, BaseTableRef} from "../../../../components/BaseTable/BaseTable.tsx";
|
||||||
|
import {MRT_TableOptions} from "mantine-react-table";
|
||||||
|
import {useProductsTableColumns} from "./columns.tsx";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
products: ProductSchema[];
|
||||||
|
tableRef?: RefObject<BaseTableRef<ProductSchema>>
|
||||||
|
}
|
||||||
|
const ProductsTable: FC<Props> = ({products, tableRef}) => {
|
||||||
|
const columns = useProductsTableColumns();
|
||||||
|
return (
|
||||||
|
<BaseTable
|
||||||
|
ref={tableRef}
|
||||||
|
data={products}
|
||||||
|
columns={columns}
|
||||||
|
restProps={{
|
||||||
|
enableColumnActions: false,
|
||||||
|
manualPagination: true,
|
||||||
|
enableBottomToolbar: true,
|
||||||
|
enablePagination: true
|
||||||
|
} as MRT_TableOptions<ProductSchema>}
|
||||||
|
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ProductsTable;
|
||||||
20
src/pages/ProductsPage/components/ProductsTable/columns.tsx
Normal file
20
src/pages/ProductsPage/components/ProductsTable/columns.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import {useMemo} from "react";
|
||||||
|
import {MRT_ColumnDef} from "mantine-react-table";
|
||||||
|
import {ProductSchema} from "../../../../client";
|
||||||
|
|
||||||
|
export const useProductsTableColumns = () => {
|
||||||
|
return useMemo<MRT_ColumnDef<ProductSchema>[]>(() => [
|
||||||
|
{
|
||||||
|
accessorKey: "article",
|
||||||
|
header: "Артикул",
|
||||||
|
enableSorting: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "name",
|
||||||
|
header: "Название",
|
||||||
|
enableSorting: false,
|
||||||
|
|
||||||
|
},
|
||||||
|
], []);
|
||||||
|
|
||||||
|
}
|
||||||
19
src/pages/ProductsPage/hooks/useProductsList.tsx
Normal file
19
src/pages/ProductsPage/hooks/useProductsList.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import {useQuery} from "@tanstack/react-query";
|
||||||
|
import {ProductService} from "../../../client";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
clientId: number,
|
||||||
|
page: number,
|
||||||
|
itemsPerPage: number,
|
||||||
|
}
|
||||||
|
const useServicesList = (props: Props) => {
|
||||||
|
const {clientId, page, itemsPerPage} = props;
|
||||||
|
const {isPending, error, data, refetch} = useQuery({
|
||||||
|
queryKey: ['getAllServices', clientId, page, itemsPerPage],
|
||||||
|
queryFn: () => ProductService.getProductsByClientId({clientId, page, itemsPerPage})
|
||||||
|
});
|
||||||
|
const products = isPending || error || !data ? [] : data.products;
|
||||||
|
|
||||||
|
return {products, refetch}
|
||||||
|
}
|
||||||
|
export default useServicesList;
|
||||||
1
src/pages/ProductsPage/index.ts
Normal file
1
src/pages/ProductsPage/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export {ProductsPage} from './ui/ProductsPage';
|
||||||
17
src/pages/ProductsPage/ui/ProductsPage.module.css
Normal file
17
src/pages/ProductsPage/ui/ProductsPage.module.css
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
gap: rem(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.body-container {
|
||||||
|
padding: rem(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-panel {
|
||||||
|
padding: rem(5);
|
||||||
|
gap: rem(10);
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
40
src/pages/ProductsPage/ui/ProductsPage.tsx
Normal file
40
src/pages/ProductsPage/ui/ProductsPage.tsx
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
|
||||||
|
import {FC, useState} from "react";
|
||||||
|
import styles from './ProductsPage.module.css';
|
||||||
|
import {Drawer} from "@mantine/core";
|
||||||
|
import {useDisclosure} from "@mantine/hooks";
|
||||||
|
import ClientSelect from "../../../components/Selects/ClientSelect/ClientSelect.tsx";
|
||||||
|
import ProductsTable from "../components/ProductsTable/ProductsTable.tsx";
|
||||||
|
import useProductsList from "../hooks/useProductsList.tsx";
|
||||||
|
|
||||||
|
export const ProductsPage: FC = () => {
|
||||||
|
const [opened, {open, close}] = useDisclosure(false);
|
||||||
|
const [clientId, setClientId] = useState(-1);
|
||||||
|
const {products} = useProductsList({clientId, page: 0, itemsPerPage: 10});
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Drawer
|
||||||
|
opened={opened}
|
||||||
|
onClose={close}
|
||||||
|
position={"right"}
|
||||||
|
size={"100%"}
|
||||||
|
/>
|
||||||
|
<div className={styles['container']}>
|
||||||
|
|
||||||
|
<PageBlock>
|
||||||
|
<div className={styles['top-panel']}>
|
||||||
|
|
||||||
|
<ClientSelect onChange={event => setClientId(event.id)}/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</PageBlock>
|
||||||
|
<PageBlock>
|
||||||
|
<div className={styles['body-container']}>
|
||||||
|
<ProductsTable products={products}/>
|
||||||
|
</div>
|
||||||
|
</PageBlock>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ import { Route as rootRoute } from './routes/__root'
|
|||||||
|
|
||||||
const TestLazyImport = createFileRoute('/test')()
|
const TestLazyImport = createFileRoute('/test')()
|
||||||
const ServicesLazyImport = createFileRoute('/services')()
|
const ServicesLazyImport = createFileRoute('/services')()
|
||||||
|
const ProductsLazyImport = createFileRoute('/products')()
|
||||||
const LoginLazyImport = createFileRoute('/login')()
|
const LoginLazyImport = createFileRoute('/login')()
|
||||||
const LeadsLazyImport = createFileRoute('/leads')()
|
const LeadsLazyImport = createFileRoute('/leads')()
|
||||||
const ClientsLazyImport = createFileRoute('/clients')()
|
const ClientsLazyImport = createFileRoute('/clients')()
|
||||||
@@ -35,6 +36,11 @@ const ServicesLazyRoute = ServicesLazyImport.update({
|
|||||||
getParentRoute: () => rootRoute,
|
getParentRoute: () => rootRoute,
|
||||||
} as any).lazy(() => import('./routes/services.lazy').then((d) => d.Route))
|
} as any).lazy(() => import('./routes/services.lazy').then((d) => d.Route))
|
||||||
|
|
||||||
|
const ProductsLazyRoute = ProductsLazyImport.update({
|
||||||
|
path: '/products',
|
||||||
|
getParentRoute: () => rootRoute,
|
||||||
|
} as any).lazy(() => import('./routes/products.lazy').then((d) => d.Route))
|
||||||
|
|
||||||
const LoginLazyRoute = LoginLazyImport.update({
|
const LoginLazyRoute = LoginLazyImport.update({
|
||||||
path: '/login',
|
path: '/login',
|
||||||
getParentRoute: () => rootRoute,
|
getParentRoute: () => rootRoute,
|
||||||
@@ -75,6 +81,10 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof LoginLazyImport
|
preLoaderRoute: typeof LoginLazyImport
|
||||||
parentRoute: typeof rootRoute
|
parentRoute: typeof rootRoute
|
||||||
}
|
}
|
||||||
|
'/products': {
|
||||||
|
preLoaderRoute: typeof ProductsLazyImport
|
||||||
|
parentRoute: typeof rootRoute
|
||||||
|
}
|
||||||
'/services': {
|
'/services': {
|
||||||
preLoaderRoute: typeof ServicesLazyImport
|
preLoaderRoute: typeof ServicesLazyImport
|
||||||
parentRoute: typeof rootRoute
|
parentRoute: typeof rootRoute
|
||||||
@@ -93,6 +103,7 @@ export const routeTree = rootRoute.addChildren([
|
|||||||
ClientsLazyRoute,
|
ClientsLazyRoute,
|
||||||
LeadsLazyRoute,
|
LeadsLazyRoute,
|
||||||
LoginLazyRoute,
|
LoginLazyRoute,
|
||||||
|
ProductsLazyRoute,
|
||||||
ServicesLazyRoute,
|
ServicesLazyRoute,
|
||||||
TestLazyRoute,
|
TestLazyRoute,
|
||||||
])
|
])
|
||||||
|
|||||||
6
src/routes/products.lazy.tsx
Normal file
6
src/routes/products.lazy.tsx
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import {createLazyFileRoute} from "@tanstack/react-router";
|
||||||
|
import {ProductsPage} from "../pages/ProductsPage";
|
||||||
|
|
||||||
|
export const Route = createLazyFileRoute('/products')({
|
||||||
|
component: ProductsPage
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user