feat: products page
This commit is contained in:
@ -2,6 +2,7 @@ import { FC } from "react";
|
||||
import { Button, Flex } from "@mantine/core";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { useFulfillmentBaseContext } from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/contexts/FulfillmentBaseContext";
|
||||
import { notifications } from "@/lib/notifications";
|
||||
|
||||
const ProductsActions: FC = () => {
|
||||
const { deal, dealProductsList, productsCrud, dealProductsCrud } =
|
||||
@ -20,6 +21,11 @@ const ProductsActions: FC = () => {
|
||||
};
|
||||
|
||||
const onCreateDealProductClick = () => {
|
||||
if (!deal.client) {
|
||||
notifications.error({ message: "Выберите клиента для сделки" });
|
||||
return;
|
||||
}
|
||||
|
||||
const productIdsToExclude = dealProductsList.dealProducts.map(
|
||||
product => product.product.id
|
||||
);
|
||||
@ -33,7 +39,7 @@ const ProductsActions: FC = () => {
|
||||
dealProductsCrud.onCreate({ ...values, dealId: deal.id }),
|
||||
productIdsToExclude,
|
||||
isEditing: false,
|
||||
clientId: 0, // TODO add clients
|
||||
clientId: deal.client.id,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@ -3,6 +3,7 @@ import { IconPlus } from "@tabler/icons-react";
|
||||
import { ButtonProps, Text } from "@mantine/core";
|
||||
import { modals } from "@mantine/modals";
|
||||
import InlineButton from "@/components/ui/InlineButton/InlineButton";
|
||||
import { notifications } from "@/lib/notifications";
|
||||
import { useFulfillmentBaseContext } from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/contexts/FulfillmentBaseContext";
|
||||
|
||||
type Props = ButtonProps;
|
||||
@ -12,6 +13,11 @@ const AddDealProductButton: FC<Props> = props => {
|
||||
useFulfillmentBaseContext();
|
||||
|
||||
const onCreateClick = () => {
|
||||
if (!deal.client) {
|
||||
notifications.error({ message: "Выберите клиента для сделки" });
|
||||
return;
|
||||
}
|
||||
|
||||
const productIdsToExclude = dealProductsList.dealProducts.map(
|
||||
product => product.product.id
|
||||
);
|
||||
@ -25,7 +31,7 @@ const AddDealProductButton: FC<Props> = props => {
|
||||
dealProductsCrud.onCreate({ ...values, dealId: deal.id }),
|
||||
productIdsToExclude,
|
||||
isEditing: false,
|
||||
clientId: 0, // TODO add clients
|
||||
clientId: deal.client.id,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@ -11,6 +11,7 @@ import {
|
||||
} from "@mantine/core";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { DealProductSchema } from "@/lib/client";
|
||||
import { notifications } from "@/lib/notifications";
|
||||
import ProductFieldsList from "@/modules/dealModularEditorTabs/FulfillmentBase/desktop/components/ProductView/components/ProductFieldsList";
|
||||
import ProductMenu from "@/modules/dealModularEditorTabs/FulfillmentBase/mobile/components/ProductMenu/ProductMenu";
|
||||
import ProductServicesTable from "@/modules/dealModularEditorTabs/FulfillmentBase/mobile/components/ProductServicesTable/ProductServicesTable";
|
||||
@ -22,9 +23,14 @@ type Props = {
|
||||
};
|
||||
|
||||
const DealProductView: FC<Props> = ({ dealProduct }) => {
|
||||
const { dealProductsCrud } = useFulfillmentBaseContext();
|
||||
const { dealProductsCrud, deal } = useFulfillmentBaseContext();
|
||||
|
||||
const onChangeDealProductClick = () => {
|
||||
if (!deal.client) {
|
||||
notifications.error({ message: "Выберите клиента для сделки" });
|
||||
return;
|
||||
}
|
||||
|
||||
modals.openContextModal({
|
||||
modal: "dealProductEditorModal",
|
||||
title: "Добавление товара",
|
||||
@ -38,7 +44,7 @@ const DealProductView: FC<Props> = ({ dealProduct }) => {
|
||||
),
|
||||
entity: dealProduct,
|
||||
isEditing: true,
|
||||
clientId: 0, // TODO add clients
|
||||
clientId: deal.client.id,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@ -21,7 +21,7 @@ const ProductSelect: FC<Props> = (props: Props) => {
|
||||
const [searchValue, setSearchValue] = useState("");
|
||||
const [debounced] = useDebouncedValue(searchValue, 500);
|
||||
const { products, isLoading } = useProductsList({
|
||||
// clientId: props.clientId,
|
||||
clientId: props.clientId,
|
||||
searchInput: debounced,
|
||||
page: 0,
|
||||
itemsPerPage: MAX_PRODUCTS,
|
||||
|
||||
@ -40,7 +40,9 @@ type Props = {
|
||||
const useFulfillmentBaseContextState = ({
|
||||
deal,
|
||||
}: Props): FulfillmentBaseContextState => {
|
||||
const productQueryKey = getProductsQueryKey();
|
||||
const productQueryKey = getProductsQueryKey({
|
||||
query: { clientId: deal.client?.id },
|
||||
});
|
||||
const productsCrud = useProductsCrud({ queryKey: productQueryKey });
|
||||
|
||||
const dealProductsList = useDealProductsList({ dealId: deal.id });
|
||||
|
||||
@ -1,22 +1,40 @@
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { ProductSchema } from "@/lib/client";
|
||||
import { PaginationInfoSchema, ProductSchema } from "@/lib/client";
|
||||
import {
|
||||
getProductsOptions,
|
||||
getProductsQueryKey,
|
||||
} from "@/lib/client/@tanstack/react-query.gen";
|
||||
|
||||
type Props = {
|
||||
clientId?: number;
|
||||
searchInput: string;
|
||||
page?: number;
|
||||
itemsPerPage?: number;
|
||||
};
|
||||
|
||||
const useProductsList = ({ searchInput, page, itemsPerPage }: Props) => {
|
||||
type ReturnType = {
|
||||
products: ProductSchema[];
|
||||
paginationInfo?: PaginationInfoSchema;
|
||||
setProducts: (products: ProductSchema[]) => void;
|
||||
refetch: () => void;
|
||||
queryKey: any[];
|
||||
isLoading: boolean;
|
||||
};
|
||||
|
||||
const useProductsList = ({
|
||||
clientId,
|
||||
searchInput,
|
||||
page,
|
||||
itemsPerPage,
|
||||
}: Props): ReturnType => {
|
||||
const queryClient = useQueryClient();
|
||||
const options = {
|
||||
query: { searchInput, page, itemsPerPage },
|
||||
query: { clientId, searchInput, page, itemsPerPage },
|
||||
};
|
||||
const { data, refetch, isLoading } = useQuery(getProductsOptions(options));
|
||||
const { data, refetch, isLoading } = useQuery({
|
||||
...getProductsOptions(options),
|
||||
enabled: !!clientId,
|
||||
});
|
||||
|
||||
const queryKey = getProductsQueryKey(options);
|
||||
|
||||
@ -32,6 +50,7 @@ const useProductsList = ({ searchInput, page, itemsPerPage }: Props) => {
|
||||
|
||||
return {
|
||||
products: data?.items ?? [],
|
||||
paginationInfo: data?.paginationInfo,
|
||||
setProducts,
|
||||
refetch,
|
||||
queryKey,
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import { Fieldset, Flex, TextInput } from "@mantine/core";
|
||||
import { Fieldset, Flex, Stack, TagsInput, TextInput } from "@mantine/core";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { ContextModalProps } from "@mantine/modals";
|
||||
import BarcodeTemplateSelect from "@/app/products/components/shared/BarcodeTemplateSelect/BarcodeTemplateSelect";
|
||||
import {
|
||||
BarcodeTemplateSchema,
|
||||
CreateProductSchema,
|
||||
ProductSchema,
|
||||
UpdateProductSchema,
|
||||
@ -18,6 +20,14 @@ type Props = CreateEditFormProps<
|
||||
CreateProductSchema,
|
||||
UpdateProductSchema,
|
||||
ProductSchema
|
||||
> & {
|
||||
clientId: number;
|
||||
};
|
||||
|
||||
type ProductForm = Partial<
|
||||
ProductSchema & {
|
||||
barcodeTemplate: BarcodeTemplateSchema;
|
||||
}
|
||||
>;
|
||||
|
||||
const ProductEditorModal = ({
|
||||
@ -27,7 +37,7 @@ const ProductEditorModal = ({
|
||||
}: ContextModalProps<Props>) => {
|
||||
const isEditing = "entity" in innerProps;
|
||||
|
||||
const initialValues: Partial<ProductSchema> = isEditing
|
||||
const initialValues: ProductForm = isEditing
|
||||
? innerProps.entity!
|
||||
: {
|
||||
name: "",
|
||||
@ -38,6 +48,9 @@ const ProductEditorModal = ({
|
||||
color: "",
|
||||
size: "",
|
||||
additionalInfo: "",
|
||||
clientId: innerProps.clientId,
|
||||
barcodeTemplate: undefined,
|
||||
barcodeTemplateId: undefined,
|
||||
};
|
||||
|
||||
const form = useForm<Partial<ProductSchema>>({
|
||||
@ -50,60 +63,83 @@ const ProductEditorModal = ({
|
||||
},
|
||||
});
|
||||
|
||||
const onClose = () => context.closeContextModal(id);
|
||||
|
||||
return (
|
||||
<BaseFormModal
|
||||
{...innerProps}
|
||||
form={form}
|
||||
closeOnSubmit
|
||||
onClose={onClose}>
|
||||
onClose={() => context.closeContextModal(id)}>
|
||||
<Flex
|
||||
gap={"xs"}
|
||||
direction={"column"}>
|
||||
<Fieldset legend={"Основные характеристики"}>
|
||||
<TextInput
|
||||
placeholder={"Введите название товара"}
|
||||
label={"Название товара"}
|
||||
{...form.getInputProps("name")}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder={"Введите артикул"}
|
||||
label={"Артикул"}
|
||||
{...form.getInputProps("article")}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder={"Введите складской артикул"}
|
||||
label={"Складской артикул"}
|
||||
{...form.getInputProps("factoryArticle")}
|
||||
/>
|
||||
<Stack gap={"xs"}>
|
||||
<TextInput
|
||||
placeholder={"Введите название товара"}
|
||||
label={"Название товара"}
|
||||
{...form.getInputProps("name")}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder={"Введите артикул"}
|
||||
label={"Артикул"}
|
||||
{...form.getInputProps("article")}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder={"Введите складской артикул"}
|
||||
label={"Складской артикул"}
|
||||
{...form.getInputProps("factoryArticle")}
|
||||
/>
|
||||
<BarcodeTemplateSelect
|
||||
placeholder={"Выберите шаблон штрихкода"}
|
||||
label={"Шаблон штрихкода"}
|
||||
{...form.getInputProps("barcodeTemplate")}
|
||||
onChange={template => {
|
||||
form.setFieldValue("barcodeTemplate", template);
|
||||
form.setFieldValue(
|
||||
"barcodeTemplateId",
|
||||
template?.id
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<TagsInput
|
||||
placeholder={
|
||||
!form.values.barcodes?.length
|
||||
? "Добавьте штрихкоды к товару"
|
||||
: ""
|
||||
}
|
||||
label={"Штрихкоды"}
|
||||
{...form.getInputProps("barcodes")}
|
||||
/>
|
||||
</Stack>
|
||||
</Fieldset>
|
||||
<Fieldset legend={"Дополнительные характеристики"}>
|
||||
<TextInput
|
||||
placeholder={"Введите бренд"}
|
||||
label={"Бренд"}
|
||||
{...form.getInputProps("brand")}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder={"Введите состав"}
|
||||
label={"Состав"}
|
||||
{...form.getInputProps("composition")}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder={"Введите цвет"}
|
||||
label={"Цвет"}
|
||||
{...form.getInputProps("color")}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder={"Введите размер"}
|
||||
label={"Размер"}
|
||||
{...form.getInputProps("size")}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder={"Введите доп. информацию"}
|
||||
label={"Доп. информация"}
|
||||
{...form.getInputProps("additionalInfo")}
|
||||
/>
|
||||
<Stack gap={"xs"}>
|
||||
<TextInput
|
||||
placeholder={"Введите бренд"}
|
||||
label={"Бренд"}
|
||||
{...form.getInputProps("brand")}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder={"Введите состав"}
|
||||
label={"Состав"}
|
||||
{...form.getInputProps("composition")}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder={"Введите цвет"}
|
||||
label={"Цвет"}
|
||||
{...form.getInputProps("color")}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder={"Введите размер"}
|
||||
label={"Размер"}
|
||||
{...form.getInputProps("size")}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder={"Введите доп. информацию"}
|
||||
label={"Доп. информация"}
|
||||
{...form.getInputProps("additionalInfo")}
|
||||
/>
|
||||
</Stack>
|
||||
</Fieldset>
|
||||
{isEditing && (
|
||||
<ProductImageDropzone
|
||||
|
||||
Reference in New Issue
Block a user