This commit is contained in:
2024-03-28 08:22:27 +03:00
parent c9f3d4ee12
commit 806e73bb5a
27 changed files with 432 additions and 92 deletions

View File

@@ -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<Props> = ({onSubmit, onCancel}) => {
</div>
<div className={styles['inputs']}>
<ClientSelect
<ClientAutocomplete
withAddress
nameRestProps={form.getInputProps('client_name')}
addressRestProps={form.getInputProps('client_address')}

View File

@@ -23,7 +23,7 @@ const DealSummaryCard: FC<Props> = ({dealSummary}) => {
</div>
<div className={styles['flex-item']}>
<Text size={"sm"} c={"gray.6"}>
228 руб
{dealSummary.total_price.toLocaleString('ru-RU')} руб
</Text>
</div>
</div>

View File

@@ -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() {

View File

@@ -2,5 +2,8 @@
border-radius: rem(20);
background-color: var(--mantine-color-body);
padding: rem(10);
}
.container-fluid {
flex: 1;
}

View File

@@ -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<Props> = ({children}) => {
export const PageBlock: FC<Props> = ({children, fluid = true}) => {
return (
<div className={styles['container']}>
<div className={classNames(styles['container'], fluid && styles['container-fluid'])}>
{children}
</div>
)

View File

@@ -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;

View File

@@ -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<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]);
const ClientSelect: FC<Props> = ({value, onChange, withLabel = false}) => {
const {clients} = useClientsList();
const options = clients.map(client => ({label: client.name, value: client.id.toString()}))
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})
}}
/>
}
</>
<Select
placeholder={"Выберите клиента"}
value={value && options.find(client => client.value == value.id.toString())?.value}
onChange={event => {
if (!event) return;
const client = clients.find(client => client.id == parseInt(event));
if (!client) return;
onChange(client);
}}
data={options}
label={withLabel && "Клиент"}
/>
)
}
export default ClientSelect;