feat: product images upload and display

This commit is contained in:
2025-10-20 16:13:05 +04:00
parent 8cc11bca67
commit 82f08b4f83
12 changed files with 380 additions and 226 deletions

View File

@ -1,6 +1,7 @@
import { FC } from "react";
import { DropzoneProps, FileWithPath } from "@mantine/dropzone";
import ImageDropzone from "@/components/ui/ImageDropzone/ImageDropzone";
import { uploadProductImage } from "@/lib/client";
import { notifications } from "@/lib/notifications";
import BaseFormInputProps from "@/utils/baseFormInputProps";
import useImageDropzone from "./useImageDropzone";
@ -31,30 +32,31 @@ const ProductImageDropzone: FC<Props> = ({
setIsLoading(true);
// TODO SEND REQUEST
uploadProductImage({
path: {
productId,
},
body: {
upload_file: file,
},
})
.then(({ data }) => {
notifications.success({ message: data?.message });
setIsLoading(false);
// ProductService.uploadProductImage({
// productId,
// formData: {
// upload_file: file,
// },
// })
// .then(({ ok, message, imageUrl }) => {
// notifications.guess(ok, { message });
// setIsLoading(false);
//
// if (!ok || !imageUrl) {
// setShowDropzone(true);
// return;
// }
// imageUrlInputProps?.onChange(imageUrl);
// setShowDropzone(false);
// })
// .catch(error => {
// notifications.error({ message: error.toString() });
// setShowDropzone(true);
// setIsLoading(false);
// });
if (!data?.imageUrl) {
setShowDropzone(true);
return;
}
imageUrlInputProps?.onChange(data?.imageUrl);
setShowDropzone(false);
})
.catch(err => {
console.log(err);
notifications.error({ message: err.toString() });
setShowDropzone(true);
setIsLoading(false);
});
};
return (

View File

@ -107,12 +107,12 @@ const ProductView: FC<Props> = ({ dealProduct }) => {
<Stack
flex={2}
gap={"sm"}>
{!dealProduct.product && (
{dealProduct.product?.imageUrl && (
<Image
flex={1}
radius={"md"}
fit={"cover"}
// src={dealProduct.product.imageUrl}
src={dealProduct.product.imageUrl}
/>
)}
<Title order={3}>{dealProduct.product.name}</Title>

View File

@ -1,5 +1,6 @@
"use client";
import { useState } from "react";
import { Fieldset, Flex, Stack, TagsInput, TextInput } from "@mantine/core";
import { useForm } from "@mantine/form";
import { ContextModalProps } from "@mantine/modals";
@ -14,6 +15,9 @@ import BaseFormModal, {
CreateEditFormProps,
} from "@/modals/base/BaseFormModal/BaseFormModal";
import ProductImageDropzone from "@/modules/dealModularEditorTabs/FulfillmentBase/desktop/components/ProductImageDropzone/ProductImageDropzone";
import ProductEditorSegmentedControl, {
ProductEditorTab,
} from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/modals/ProductEditorModal/components/ProductEditorSegmentedControl";
import BaseFormInputProps from "@/utils/baseFormInputProps";
type Props = CreateEditFormProps<
@ -35,6 +39,9 @@ const ProductEditorModal = ({
id,
innerProps,
}: ContextModalProps<Props>) => {
const [editorTab, setEditorTab] = useState<ProductEditorTab>(
ProductEditorTab.CHARACTERISTICS
);
const isEditing = "entity" in innerProps;
const initialValues: ProductForm = isEditing
@ -63,94 +70,108 @@ const ProductEditorModal = ({
},
});
const characteristicsTab = (
<>
<Fieldset legend={"Основные характеристики"}>
<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={"Дополнительные характеристики"}>
<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>
</>
);
const imageTab = isEditing && (
<ProductImageDropzone
imageUrlInputProps={
form.getInputProps("imageUrl") as BaseFormInputProps<string>
}
productId={innerProps.entity.id}
/>
);
return (
<BaseFormModal
{...innerProps}
form={form}
closeOnSubmit
actionsEnabled={editorTab === ProductEditorTab.CHARACTERISTICS}
onClose={() => context.closeContextModal(id)}>
<Flex
gap={"xs"}
direction={"column"}>
<Fieldset legend={"Основные характеристики"}>
<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={"Дополнительные характеристики"}>
<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
imageUrlInputProps={
form.getInputProps(
"imageUrl"
) as BaseFormInputProps<string>
}
productId={innerProps.entity.id}
<ProductEditorSegmentedControl
value={editorTab}
onChange={setEditorTab}
/>
)}
{editorTab === ProductEditorTab.CHARACTERISTICS
? characteristicsTab
: imageTab}
</Flex>
</BaseFormModal>
);

View File

@ -0,0 +1,31 @@
import { FC } from "react";
import BaseSegmentedControl, {
BaseSegmentedControlProps,
} from "@/components/ui/BaseSegmentedControl/BaseSegmentedControl";
export enum ProductEditorTab {
CHARACTERISTICS,
IMAGES,
}
type Props = Omit<BaseSegmentedControlProps<ProductEditorTab>, "data">;
const data = [
{
label: "Характеристики",
value: ProductEditorTab.CHARACTERISTICS,
},
{
label: "Изображение",
value: ProductEditorTab.IMAGES,
},
];
const ProductEditorSegmentedControl: FC<Props> = props => (
<BaseSegmentedControl
data={data}
{...props}
/>
);
export default ProductEditorSegmentedControl;