feat: prettier

This commit is contained in:
2024-09-27 04:47:04 +03:00
parent c5f839d9ef
commit de4fe450ab
253 changed files with 11322 additions and 10004 deletions

View File

@@ -5,5 +5,5 @@
}
.pagination {
align-self: flex-end;
}
align-self: flex-end;
}

View File

@@ -1,74 +1,84 @@
import {ProductSchema} from "../../../../client";
import {FC} from "react";
import {BaseTable} from "../../../../components/BaseTable/BaseTable.tsx";
import {MRT_TableOptions} from "mantine-react-table";
import {useProductsTableColumns} from "./columns.tsx";
import {ActionIcon, Flex, Tooltip} from "@mantine/core";
import {IconBarcode, IconEdit, IconTrash} from "@tabler/icons-react";
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
import {modals} from "@mantine/modals";
import { ProductSchema } from "../../../../client";
import { FC } from "react";
import { BaseTable } from "../../../../components/BaseTable/BaseTable.tsx";
import { MRT_TableOptions } from "mantine-react-table";
import { useProductsTableColumns } from "./columns.tsx";
import { ActionIcon, Flex, Tooltip } from "@mantine/core";
import { IconBarcode, IconEdit, IconTrash } from "@tabler/icons-react";
import { CRUDTableProps } from "../../../../types/CRUDTable.tsx";
import { modals } from "@mantine/modals";
const ProductsTable: FC<CRUDTableProps<ProductSchema>> = ({items, onDelete, onChange, tableRef}) => {
const ProductsTable: FC<CRUDTableProps<ProductSchema>> = ({
items,
onDelete,
onChange,
tableRef,
}) => {
const columns = useProductsTableColumns();
const onEditClick = (product: ProductSchema) => {
if (!onChange) return;
modals.openContextModal({
modal: "createProduct",
title: 'Создание товара',
title: "Создание товара",
withCloseButton: false,
innerProps: {
onChange: (newProduct) => onChange(newProduct),
onChange: newProduct => onChange(newProduct),
product: product,
}
})
}
},
});
};
const onPrintBarcodeClick = (product: ProductSchema) => {
modals.openContextModal({
modal: "printBarcode",
title: 'Печать штрихкода',
title: "Печать штрихкода",
withCloseButton: true,
innerProps: {
productId: product.id
}
})
}
productId: product.id,
},
});
};
return (
<BaseTable
ref={tableRef}
data={items}
columns={columns}
restProps={{
enableColumnActions: false,
enableRowActions: true,
renderRowActions: ({row}) => (
<Flex gap="md">
<Tooltip label="Печать штрихкода">
<ActionIcon
onClick={() => onPrintBarcodeClick(row.original)}
variant={"default"}>
<IconBarcode/>
</ActionIcon>
</Tooltip>
<Tooltip label="Редактировать">
<ActionIcon
onClick={() => onEditClick(row.original)}
variant={"default"}>
<IconEdit/>
</ActionIcon>
</Tooltip>
<Tooltip label="Удалить">
<ActionIcon onClick={() => {
if (onDelete) onDelete(row.original);
}} variant={"default"}>
<IconTrash/>
</ActionIcon>
</Tooltip>
</Flex>
)
} as MRT_TableOptions<ProductSchema>}
restProps={
{
enableColumnActions: false,
enableRowActions: true,
renderRowActions: ({ row }) => (
<Flex gap="md">
<Tooltip label="Печать штрихкода">
<ActionIcon
onClick={() =>
onPrintBarcodeClick(row.original)
}
variant={"default"}>
<IconBarcode />
</ActionIcon>
</Tooltip>
<Tooltip label="Редактировать">
<ActionIcon
onClick={() => onEditClick(row.original)}
variant={"default"}>
<IconEdit />
</ActionIcon>
</Tooltip>
<Tooltip label="Удалить">
<ActionIcon
onClick={() => {
if (onDelete) onDelete(row.original);
}}
variant={"default"}>
<IconTrash />
</ActionIcon>
</Tooltip>
</Flex>
),
} as MRT_TableOptions<ProductSchema>
}
/>
)
}
);
};
export default ProductsTable;
export default ProductsTable;

View File

@@ -1,73 +1,76 @@
import {useMemo} from "react";
import {MRT_ColumnDef} from "mantine-react-table";
import {ProductSchema} from "../../../../client";
import {List, Spoiler, useMantineTheme} from "@mantine/core";
import { useMemo } from "react";
import { MRT_ColumnDef } from "mantine-react-table";
import { ProductSchema } from "../../../../client";
import { List, Spoiler, useMantineTheme } from "@mantine/core";
export const useProductsTableColumns = () => {
const theme = useMantineTheme();
return useMemo<MRT_ColumnDef<ProductSchema>[]>(() => [
{
accessorKey: "article",
header: "Артикул",
enableSorting: false,
},
{
accessorKey: "name",
header: "Название",
enableSorting: false,
},
{
accessorKey: "barcodes",
header: "Штрихкоды",
Cell: ({cell}) => {
return (
<List size={"sm"}>
<Spoiler maxHeight={parseFloat(theme.lineHeights.sm) * 25}
showLabel={"Показать все"}
hideLabel={"Скрыть"}>
{cell.getValue<string[]>().map(barcode => (
<List.Item key={barcode}>
{barcode}
</List.Item>
))}
</Spoiler>
</List>
)
return useMemo<MRT_ColumnDef<ProductSchema>[]>(
() => [
{
accessorKey: "article",
header: "Артикул",
enableSorting: false,
},
enableSorting: false,
},
{
accessorKey: "barcodeTemplate.name",
header: "Шаблон штрихкода",
enableSorting: false,
},
{
accessorKey: "brand",
header: "Бренд",
enableSorting: false
},
{
accessorKey: "composition",
header: "Состав",
enableSorting: false,
},
{
accessorKey: "color",
header: "Цвет",
enableSorting: false
},
{
accessorKey: "size",
header: "Размер",
enableSorting: false
},
{
accessorKey: "additionalInfo",
header: "Доп. информация",
enableSorting: false
}
], []);
}
{
accessorKey: "name",
header: "Название",
enableSorting: false,
},
{
accessorKey: "barcodes",
header: "Штрихкоды",
Cell: ({ cell }) => {
return (
<List size={"sm"}>
<Spoiler
maxHeight={
parseFloat(theme.lineHeights.sm) * 25
}
showLabel={"Показать все"}
hideLabel={"Скрыть"}>
{cell.getValue<string[]>().map(barcode => (
<List.Item key={barcode}>
{barcode}
</List.Item>
))}
</Spoiler>
</List>
);
},
enableSorting: false,
},
{
accessorKey: "barcodeTemplate.name",
header: "Шаблон штрихкода",
enableSorting: false,
},
{
accessorKey: "brand",
header: "Бренд",
enableSorting: false,
},
{
accessorKey: "composition",
header: "Состав",
enableSorting: false,
},
{
accessorKey: "color",
header: "Цвет",
enableSorting: false,
},
{
accessorKey: "size",
header: "Размер",
enableSorting: false,
},
{
accessorKey: "additionalInfo",
header: "Доп. информация",
enableSorting: false,
},
],
[]
);
};

View File

@@ -1,20 +1,20 @@
import {useQuery} from "@tanstack/react-query";
import {ProductService} from "../../../client";
import { useQuery } from "@tanstack/react-query";
import { ProductService } from "../../../client";
type Props = {
clientId: number,
page?: number,
itemsPerPage?: number,
searchInput: string
}
clientId: number;
page?: number;
itemsPerPage?: number;
searchInput: string;
};
const useProductsList = (props: Props) => {
const {clientId, page, itemsPerPage, searchInput} = props;
const {data, refetch, isLoading} = useQuery({
queryKey: ['getAllServices', clientId, page, itemsPerPage, searchInput],
queryFn: () => ProductService.getProductsByClientId(props)
const { clientId, page, itemsPerPage, searchInput } = props;
const { data, refetch, isLoading } = useQuery({
queryKey: ["getAllServices", clientId, page, itemsPerPage, searchInput],
queryFn: () => ProductService.getProductsByClientId(props),
});
const products = !data ? [] : data.products;
const paginationInfo = data?.paginationInfo;
return {products, paginationInfo, refetch, isLoading}
}
export default useProductsList;
return { products, paginationInfo, refetch, isLoading };
};
export default useProductsList;

View File

@@ -1 +1 @@
export {ProductsPage} from './ui/ProductsPage';
export { ProductsPage } from "./ui/ProductsPage";

View File

@@ -1,138 +1,174 @@
import {ContextModalProps} from "@mantine/modals";
import {Button, Fieldset, Flex, rem, TagsInput, TextInput} from "@mantine/core";
import {useForm} from "@mantine/form";
import {BaseProduct, CreateProductRequest} from "../../types.ts";
import {ProductSchema} from "../../../../client";
import { ContextModalProps } from "@mantine/modals";
import {
Button,
Fieldset,
Flex,
rem,
TagsInput,
TextInput,
} from "@mantine/core";
import { useForm } from "@mantine/form";
import { BaseProduct, CreateProductRequest } from "../../types.ts";
import { ProductSchema } from "../../../../client";
import BarcodeTemplateSelect from "../../../../components/Selects/BarcodeTemplateSelect/BarcodeTemplateSelect.tsx";
import ImageDropzone from "../../../../components/ImageDropzone/ImageDropzone.tsx";
import {BaseFormInputProps} from "../../../../types/utils.ts";
import { BaseFormInputProps } from "../../../../types/utils.ts";
type CreateProps = {
clientId: number;
onCreate: (values: CreateProductRequest) => void
}
onCreate: (values: CreateProductRequest) => void;
};
type EditProps = {
product: ProductSchema,
onChange: (values: ProductSchema) => void
}
product: ProductSchema;
onChange: (values: ProductSchema) => void;
};
type Props = CreateProps | EditProps;
const CreateProductModal = ({
context,
id,
innerProps,
}: ContextModalProps<Props>) => {
const isEditProps = 'product' in innerProps;
const isCreatingProps = 'clientId' in innerProps;
const initialValues: Omit<ProductSchema, 'id'> = isEditProps ? innerProps.product : {
name: '',
article: '',
barcodes: [],
clientId: innerProps.clientId
};
const form = useForm<Omit<ProductSchema, 'id'>>({
context,
id,
innerProps,
}: ContextModalProps<Props>) => {
const isEditProps = "product" in innerProps;
const isCreatingProps = "clientId" in innerProps;
const initialValues: Omit<ProductSchema, "id"> = isEditProps
? innerProps.product
: {
name: "",
article: "",
barcodes: [],
clientId: innerProps.clientId,
};
const form = useForm<Omit<ProductSchema, "id">>({
initialValues: initialValues,
validate: {
name: (name) => name.trim() !== '' ? null : "Необходимо ввести название товара",
name: name =>
name.trim() !== "" ? null : "Необходимо ввести название товара",
// article: (article) => article.trim() !== '' ? null : "Необходимо ввести артикул",
}
})
},
});
const onCancelClick = () => {
context.closeContextModal(id);
}
};
const onSubmit = (values: BaseProduct) => {
if (isEditProps) innerProps.onChange({...innerProps.product, ...values})
if (isEditProps)
innerProps.onChange({ ...innerProps.product, ...values });
if (isCreatingProps) {
innerProps.onCreate({...values, clientId: innerProps.clientId});
innerProps.onCreate({ ...values, clientId: innerProps.clientId });
form.reset();
}
}
};
return (
<>
<form onSubmit={form.onSubmit((values) => onSubmit(values))}>
<Flex gap={rem(10)} direction={"column"}>
<form onSubmit={form.onSubmit(values => onSubmit(values))}>
<Flex
gap={rem(10)}
direction={"column"}>
<Fieldset legend={"Основные характеристики"}>
<TextInput
placeholder={"Введите название товара"}
label={"Название товара"}
{...form.getInputProps('name')}
{...form.getInputProps("name")}
/>
<TextInput
placeholder={"Введите артикул"}
label={"Артикул"}
{...form.getInputProps('article')}
{...form.getInputProps("article")}
/>
<TagsInput
placeholder={!form.values.barcodes.length ? "Добавьте штрихкоды к товару" : ""}
placeholder={
!form.values.barcodes.length
? "Добавьте штрихкоды к товару"
: ""
}
label={"Штрихкоды"}
{...form.getInputProps('barcodes')}
{...form.getInputProps("barcodes")}
/>
<BarcodeTemplateSelect
placeholder={"Выберите шаблон штрихкода"}
label={"Шаблон штрихкода"}
{...form.getInputProps('barcodeTemplate')}
{...form.getInputProps("barcodeTemplate")}
/>
</Fieldset>
<Fieldset legend={"Дополнительные характеристики"}>
<TextInput
placeholder={"Введите бренд"}
label={"Бренд"}
{...form.getInputProps('brand')}
{...form.getInputProps("brand")}
/>
<TextInput
placeholder={"Введите состав"}
label={"Состав"}
{...form.getInputProps('composition')}
{...form.getInputProps("composition")}
/>
<TextInput
placeholder={"Введите цвет"}
label={"Цвет"}
{...form.getInputProps('color')}
{...form.getInputProps("color")}
/>
<TextInput
placeholder={"Введите размер"}
label={"Размер"}
{...form.getInputProps('size')}
{...form.getInputProps("size")}
/>
<TextInput
placeholder={"Введите доп. информацию"}
label={"Доп. информация"}
{...form.getInputProps('additionalInfo')} />
{...form.getInputProps("additionalInfo")}
/>
</Fieldset>
{isEditProps &&
// <Fieldset legend={"Изображение"}>
<ImageDropzone
imageUrlInputProps={form.getInputProps('imageUrl') as BaseFormInputProps<string>}
productId={innerProps.product.id}
/>
{
isEditProps && (
// <Fieldset legend={"Изображение"}>
<ImageDropzone
imageUrlInputProps={
form.getInputProps(
"imageUrl"
) as BaseFormInputProps<string>
}
productId={innerProps.product.id}
/>
)
// </Fieldset>
}
<Flex justify={"flex-end"} mt={rem(5)} gap={rem(10)}>
<Button onClick={() => onCancelClick()} variant={"subtle"}>Отменить</Button>
{isEditProps ?
<Flex
justify={"flex-end"}
mt={rem(5)}
gap={rem(10)}>
<Button
onClick={() => onCancelClick()}
variant={"subtle"}>
Отменить
</Button>
{isEditProps ? (
<Button
onClick={() => context.closeContextModal(id)}
type={"submit"}
variant={"default"}
>Сохранить и закрыть</Button> :
variant={"default"}>
Сохранить и закрыть
</Button>
) : (
<>
<Button
onClick={() => context.closeContextModal(id)}
onClick={() =>
context.closeContextModal(id)
}
type={"submit"}
variant={"default"}
>Создать и закрыть</Button>
variant={"default"}>
Создать и закрыть
</Button>
{/*<Button*/}
{/* type={"submit"}*/}
{/* variant={"default"}*/}
{/*>Создать</Button>*/}
</>}
</>
)}
</Flex>
</Flex>
</form>
</>
)
);
};
export default CreateProductModal;
export default CreateProductModal;

View File

@@ -2,5 +2,5 @@ export type BaseProduct = {
name: string;
article?: string | null;
barcodes: string[];
}
export type CreateProductRequest = BaseProduct & { clientId: number }
};
export type CreateProductRequest = BaseProduct & { clientId: number };

View File

@@ -5,7 +5,6 @@
gap: rem(10);
}
.body-container {
}
@@ -24,5 +23,4 @@
.table-pagination {
align-self: flex-end;
}
}

View File

@@ -1,15 +1,15 @@
import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
import {FC, useEffect, useState} from "react";
import styles from './ProductsPage.module.css';
import {Button, Flex, Pagination, rem, Text, TextInput} from "@mantine/core";
import { FC, useEffect, useState } from "react";
import styles from "./ProductsPage.module.css";
import { Button, Flex, Pagination, rem, Text, TextInput } from "@mantine/core";
import ClientSelect from "../../../components/Selects/ClientSelect/ClientSelect.tsx";
import ProductsTable from "../components/ProductsTable/ProductsTable.tsx";
import {modals} from "@mantine/modals";
import {notifications} from "../../../shared/lib/notifications.ts";
import {CreateProductRequest} from "../types.ts";
import {ProductSchema, ProductService} from "../../../client";
import { modals } from "@mantine/modals";
import { notifications } from "../../../shared/lib/notifications.ts";
import { CreateProductRequest } from "../types.ts";
import { ProductSchema, ProductService } from "../../../client";
import useProductsList from "../hooks/useProductsList.tsx";
import {useDebouncedValue} from "@mantine/hooks";
import { useDebouncedValue } from "@mantine/hooks";
export const ProductsPage: FC = () => {
const [clientId, setClientId] = useState(-1);
@@ -17,66 +17,69 @@ export const ProductsPage: FC = () => {
const [currentPage, setCurrentPage] = useState(1);
const [searchInputRaw, setSearchInputRaw] = useState<string>("");
const [searchInput] = useDebouncedValue(searchInputRaw, 500);
const {products, paginationInfo, refetch} = useProductsList({
const { products, paginationInfo, refetch } = useProductsList({
clientId,
page: currentPage - 1,
itemsPerPage: 10,
searchInput
searchInput,
});
const onProductCreate = (request: CreateProductRequest) => {
ProductService.createProduct({requestBody: request})
.then(async ({ok, message}) => {
notifications.guess(ok, {message: message});
ProductService.createProduct({ requestBody: request }).then(
async ({ ok, message }) => {
notifications.guess(ok, { message: message });
if (!ok) return;
await refetch();
});
}
}
);
};
const onProductChange = (product: ProductSchema) => {
ProductService.updateProduct({requestBody: {product}})
.then(async ({ok, message}) => {
notifications.guess(ok, {message});
ProductService.updateProduct({ requestBody: { product } }).then(
async ({ ok, message }) => {
notifications.guess(ok, { message });
if (!ok) return;
await refetch();
})
}
}
);
};
const onCreateProductClick = () => {
if (clientId < 0) {
notifications.error({message: "Необходимо выбрать клиента"});
return
notifications.error({ message: "Необходимо выбрать клиента" });
return;
}
modals.openContextModal({
modal: "createProduct",
title: 'Создание товара',
title: "Создание товара",
withCloseButton: false,
innerProps: {
clientId,
onCreate: onProductCreate
}
})
}
onCreate: onProductCreate,
},
});
};
const onDeleteClick = (product: ProductSchema) => {
modals.openConfirmModal({
title: 'Удаление товара',
title: "Удаление товара",
// centered: true,
children: (
<Text size="sm">
Вы уверены что хотите удалить товар {product.name}
</Text>
),
labels: {confirm: 'Да', cancel: "Нет"},
confirmProps: {color: 'red'},
labels: { confirm: "Да", cancel: "Нет" },
confirmProps: { color: "red" },
onConfirm: () =>
ProductService.deleteProduct({requestBody: {productId: product.id}})
.then(async ({ok, message}) => {
notifications.guess(ok, {message: message});
if (!ok) return;
await refetch();
})
ProductService.deleteProduct({
requestBody: { productId: product.id },
}).then(async ({ ok, message }) => {
notifications.guess(ok, { message: message });
if (!ok) return;
await refetch();
}),
});
}
};
useEffect(() => {
if (!paginationInfo) return;
@@ -88,36 +91,40 @@ export const ProductsPage: FC = () => {
return (
<>
<div className={styles['container']}>
<div className={styles["container"]}>
<PageBlock>
<div className={styles['top-panel']}>
<div className={styles["top-panel"]}>
<Flex gap={rem(10)}>
<ClientSelect onChange={event => setClientId(event.id)}/>
<ClientSelect
onChange={event => setClientId(event.id)}
/>
<Button
onClick={() => onCreateProductClick()}
variant={"default"}
>Создать</Button>
variant={"default"}>
Создать
</Button>
</Flex>
<Flex>
<TextInput
placeholder={"Артикул, название, шк"}
onChange={event => setSearchInputRaw(event.currentTarget.value)}
onChange={event =>
setSearchInputRaw(event.currentTarget.value)
}
value={searchInputRaw}
/>
</Flex>
</div>
</PageBlock>
<PageBlock>
<div className={styles['body-container']}>
<div className={styles['table-container']}>
<div className={styles["body-container"]}>
<div className={styles["table-container"]}>
<ProductsTable
onChange={onProductChange}
onDelete={onDeleteClick}
items={products}
/>
<Pagination
className={styles['table-pagination']}
className={styles["table-pagination"]}
withEdges
onChange={event => setCurrentPage(event)}
total={totalPages}
@@ -128,5 +135,5 @@ export const ProductsPage: FC = () => {
</PageBlock>
</div>
</>
)
}
);
};