From 806e73bb5ab2ac1ee1df81ad48ffe46b1af5e20d Mon Sep 17 00:00:00 2001 From: fakz9 Date: Thu, 28 Mar 2024 08:22:27 +0300 Subject: [PATCH] crap --- src/client/index.ts | 8 ++ src/client/models/DealAddServicesRequest.ts | 10 ++ src/client/models/DealAddServicesResponse.ts | 9 ++ src/client/models/DealServiceSchema.ts | 9 ++ src/client/models/DealSummary.ts | 1 + src/client/models/ProductCreateRequest.ts | 10 ++ src/client/models/ProductCreateResponse.ts | 8 ++ src/client/models/ProductGetResponse.ts | 9 ++ src/client/models/ProductSchema.ts | 11 ++ src/client/services/DealService.ts | 22 ++++ src/client/services/ProductService.ts | 59 ++++++++++ .../Dnd/CreateDealForm/CreateDealFrom.tsx | 4 +- .../Dnd/DealSummaryCard/DealSummaryCard.tsx | 2 +- src/components/Navbar/Navbar.tsx | 7 +- src/components/PageBlock/PageBlock.module.css | 3 + src/components/PageBlock/PageBlock.tsx | 6 +- .../ClientAutocomplete/ClientAutocomplete.tsx | 93 +++++++++++++++ .../Selects/ClientSelect/ClientSelect.tsx | 108 ++++-------------- src/pages/PageWrapper/PageWrapper.module.css | 2 +- .../ProductsTable/ProductsTable.tsx | 29 +++++ .../components/ProductsTable/columns.tsx | 20 ++++ .../ProductsPage/hooks/useProductsList.tsx | 19 +++ src/pages/ProductsPage/index.ts | 1 + .../ProductsPage/ui/ProductsPage.module.css | 17 +++ src/pages/ProductsPage/ui/ProductsPage.tsx | 40 +++++++ src/routeTree.gen.ts | 11 ++ src/routes/products.lazy.tsx | 6 + 27 files changed, 432 insertions(+), 92 deletions(-) create mode 100644 src/client/models/DealAddServicesRequest.ts create mode 100644 src/client/models/DealAddServicesResponse.ts create mode 100644 src/client/models/DealServiceSchema.ts create mode 100644 src/client/models/ProductCreateRequest.ts create mode 100644 src/client/models/ProductCreateResponse.ts create mode 100644 src/client/models/ProductGetResponse.ts create mode 100644 src/client/models/ProductSchema.ts create mode 100644 src/client/services/ProductService.ts create mode 100644 src/components/Selects/ClientAutocomplete/ClientAutocomplete.tsx create mode 100644 src/pages/ProductsPage/components/ProductsTable/ProductsTable.tsx create mode 100644 src/pages/ProductsPage/components/ProductsTable/columns.tsx create mode 100644 src/pages/ProductsPage/hooks/useProductsList.tsx create mode 100644 src/pages/ProductsPage/index.ts create mode 100644 src/pages/ProductsPage/ui/ProductsPage.module.css create mode 100644 src/pages/ProductsPage/ui/ProductsPage.tsx create mode 100644 src/routes/products.lazy.tsx diff --git a/src/client/index.ts b/src/client/index.ts index 0a7ec4b..c10f046 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -13,14 +13,21 @@ 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 { DealAddServicesRequest } from './models/DealAddServicesRequest'; +export type { DealAddServicesResponse } from './models/DealAddServicesResponse'; export type { DealChangeStatusRequest } from './models/DealChangeStatusRequest'; export type { DealChangeStatusResponse } from './models/DealChangeStatusResponse'; export type { DealCreateRequest } from './models/DealCreateRequest'; export type { DealQuickCreateRequest } from './models/DealQuickCreateRequest'; export type { DealQuickCreateResponse } from './models/DealQuickCreateResponse'; +export type { DealServiceSchema } from './models/DealServiceSchema'; export type { DealSummary } from './models/DealSummary'; export type { DealSummaryResponse } from './models/DealSummaryResponse'; 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 { ServiceCreateCategoryRequest } from './models/ServiceCreateCategoryRequest'; export type { ServiceCreateCategoryResponse } from './models/ServiceCreateCategoryResponse'; @@ -34,4 +41,5 @@ export type { ValidationError } from './models/ValidationError'; export { AuthService } from './services/AuthService'; export { ClientService } from './services/ClientService'; export { DealService } from './services/DealService'; +export { ProductService } from './services/ProductService'; export { ServiceService } from './services/ServiceService'; diff --git a/src/client/models/DealAddServicesRequest.ts b/src/client/models/DealAddServicesRequest.ts new file mode 100644 index 0000000..980f7b6 --- /dev/null +++ b/src/client/models/DealAddServicesRequest.ts @@ -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; +}; + diff --git a/src/client/models/DealAddServicesResponse.ts b/src/client/models/DealAddServicesResponse.ts new file mode 100644 index 0000000..eb1639f --- /dev/null +++ b/src/client/models/DealAddServicesResponse.ts @@ -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; +}; + diff --git a/src/client/models/DealServiceSchema.ts b/src/client/models/DealServiceSchema.ts new file mode 100644 index 0000000..e3b354e --- /dev/null +++ b/src/client/models/DealServiceSchema.ts @@ -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; +}; + diff --git a/src/client/models/DealSummary.ts b/src/client/models/DealSummary.ts index e584b04..6902643 100644 --- a/src/client/models/DealSummary.ts +++ b/src/client/models/DealSummary.ts @@ -8,5 +8,6 @@ export type DealSummary = { client_name: string; changed_at: string; status: number; + total_price: number; }; diff --git a/src/client/models/ProductCreateRequest.ts b/src/client/models/ProductCreateRequest.ts new file mode 100644 index 0000000..7e36506 --- /dev/null +++ b/src/client/models/ProductCreateRequest.ts @@ -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; +}; + diff --git a/src/client/models/ProductCreateResponse.ts b/src/client/models/ProductCreateResponse.ts new file mode 100644 index 0000000..0c2020d --- /dev/null +++ b/src/client/models/ProductCreateResponse.ts @@ -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; +}; + diff --git a/src/client/models/ProductGetResponse.ts b/src/client/models/ProductGetResponse.ts new file mode 100644 index 0000000..112ad44 --- /dev/null +++ b/src/client/models/ProductGetResponse.ts @@ -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; +}; + diff --git a/src/client/models/ProductSchema.ts b/src/client/models/ProductSchema.ts new file mode 100644 index 0000000..4ad33c9 --- /dev/null +++ b/src/client/models/ProductSchema.ts @@ -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; +}; + diff --git a/src/client/services/DealService.ts b/src/client/services/DealService.ts index 3f8a56d..3ad156c 100644 --- a/src/client/services/DealService.ts +++ b/src/client/services/DealService.ts @@ -2,6 +2,8 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { DealAddServicesRequest } from '../models/DealAddServicesRequest'; +import type { DealAddServicesResponse } from '../models/DealAddServicesResponse'; import type { DealChangeStatusRequest } from '../models/DealChangeStatusRequest'; import type { DealChangeStatusResponse } from '../models/DealChangeStatusResponse'; import type { DealCreateRequest } from '../models/DealCreateRequest'; @@ -83,4 +85,24 @@ export class DealService { url: '/deal/summaries', }); } + /** + * Services Add + * @returns DealAddServicesResponse Successful Response + * @throws ApiError + */ + public static servicesAddDealServicesAddPost({ + requestBody, + }: { + requestBody: DealAddServicesRequest, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/deal/services/add', + body: requestBody, + mediaType: 'application/json', + errors: { + 422: `Validation Error`, + }, + }); + } } diff --git a/src/client/services/ProductService.ts b/src/client/services/ProductService.ts new file mode 100644 index 0000000..2e718a5 --- /dev/null +++ b/src/client/services/ProductService.ts @@ -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 { + 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 { + return __request(OpenAPI, { + method: 'GET', + url: '/product/get', + query: { + 'client_id': clientId, + 'page': page, + 'items_per_page': itemsPerPage, + }, + errors: { + 422: `Validation Error`, + }, + }); + } +} diff --git a/src/components/Dnd/CreateDealForm/CreateDealFrom.tsx b/src/components/Dnd/CreateDealForm/CreateDealFrom.tsx index 1f8651d..b8b249b 100644 --- a/src/components/Dnd/CreateDealForm/CreateDealFrom.tsx +++ b/src/components/Dnd/CreateDealForm/CreateDealFrom.tsx @@ -3,7 +3,7 @@ import {QuickDeal} from "../../../types/QuickDeal.ts"; import {FC} from "react"; import {useForm} from "@mantine/form"; import styles from './CreateDealForm.module.css'; -import ClientSelect from "../../Selects/ClientSelect/ClientSelect.tsx"; +import ClientAutocomplete from "../../Selects/ClientAutocomplete/ClientAutocomplete.tsx"; import {DateTimePicker} from "@mantine/dates"; type Props = { @@ -41,7 +41,7 @@ const CreateDealFrom: FC = ({onSubmit, onCancel}) => {
- = ({dealSummary}) => {
- 228 руб + {dealSummary.total_price.toLocaleString('ru-RU')} руб
diff --git a/src/components/Navbar/Navbar.tsx b/src/components/Navbar/Navbar.tsx index 439c8b7..fcf3fc8 100644 --- a/src/components/Navbar/Navbar.tsx +++ b/src/components/Navbar/Navbar.tsx @@ -1,5 +1,5 @@ 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 {useAppDispatch} from "../../redux/store.ts"; import {logout} from "../../features/authSlice.ts"; @@ -50,6 +50,11 @@ const mockdata = [ label: 'Услуги', href: '/services' }, + { + icon: IconBarcode, + label: 'Товары', + href: '/products' + } ]; export function Navbar() { diff --git a/src/components/PageBlock/PageBlock.module.css b/src/components/PageBlock/PageBlock.module.css index 6928894..e4f2d33 100644 --- a/src/components/PageBlock/PageBlock.module.css +++ b/src/components/PageBlock/PageBlock.module.css @@ -2,5 +2,8 @@ border-radius: rem(20); background-color: var(--mantine-color-body); padding: rem(10); +} + +.container-fluid { flex: 1; } \ No newline at end of file diff --git a/src/components/PageBlock/PageBlock.tsx b/src/components/PageBlock/PageBlock.tsx index b7c09c4..6b54d54 100644 --- a/src/components/PageBlock/PageBlock.tsx +++ b/src/components/PageBlock/PageBlock.tsx @@ -1,12 +1,14 @@ import {FC, ReactNode} from "react"; import styles from './PageBlock.module.css'; +import classNames from "classnames"; type Props = { children: ReactNode + fluid?: boolean; } -export const PageBlock: FC = ({children}) => { +export const PageBlock: FC = ({children, fluid = true}) => { return ( -
+
{children}
) diff --git a/src/components/Selects/ClientAutocomplete/ClientAutocomplete.tsx b/src/components/Selects/ClientAutocomplete/ClientAutocomplete.tsx new file mode 100644 index 0000000..6fe998b --- /dev/null +++ b/src/components/Selects/ClientAutocomplete/ClientAutocomplete.tsx @@ -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 = ({onSelect, addressRestProps, nameRestProps, withAddress = false}) => { + const [value, setValue] = useState(''); + const [debouncedValue] = useDebouncedValue(value, 200); + + // const [isLoading, setIsLoading] = useState(false); + const [clients, setClients] = useState([]) + const [selectedClient, selectClient] = useState(); + + 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 ( + <> + client.name)} + styles={withAddress ? { + input: { + borderBottomLeftRadius: 0, + borderBottomRightRadius: 0 + } + } : {}} + /> + + {withAddress && + { + selectClient(prevState => prevState && {...prevState, address: event.target.value}) + }} + /> + } + + + ) +} +export default ClientAutocomplete; \ No newline at end of file diff --git a/src/components/Selects/ClientSelect/ClientSelect.tsx b/src/components/Selects/ClientSelect/ClientSelect.tsx index eeca03c..a6ce90f 100644 --- a/src/components/Selects/ClientSelect/ClientSelect.tsx +++ b/src/components/Selects/ClientSelect/ClientSelect.tsx @@ -1,93 +1,31 @@ -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"; +import {FC} from "react"; +import {Select} from "@mantine/core"; +import {ClientSchema} from "../../../client"; +import useClientsList from "../../../pages/ClientsPage/hooks/useClientsList.tsx"; + type Props = { - onSelect?: (client: Client) => void; - withAddress?: boolean; - nameRestProps?: AutocompleteProps; - addressRestProps?: TextInputProps; + value?: ClientSchema; + onChange: (client: ClientSchema) => void; + withLabel?: boolean; } -const ClientSelect: FC = ({onSelect, addressRestProps, nameRestProps, withAddress = false}) => { - const [value, setValue] = useState(''); - const [debouncedValue] = useDebouncedValue(value, 200); - - // const [isLoading, setIsLoading] = useState(false); - const [clients, setClients] = useState([]) - const [selectedClient, selectClient] = useState(); - - 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]); +const ClientSelect: FC = ({value, onChange, withLabel = false}) => { + const {clients} = useClientsList(); + const options = clients.map(client => ({label: client.name, value: client.id.toString()})) return ( - <> - client.name)} - styles={withAddress ? { - input: { - borderBottomLeftRadius: 0, - borderBottomRightRadius: 0 - } - } : {}} - /> - - {withAddress && - { - selectClient(prevState => prevState && {...prevState, address: event.target.value}) - }} - /> - } - +