feat: product images upload and display
This commit is contained in:
@ -47,7 +47,7 @@ const ServicesKitEditorModal = ({
|
||||
{...form.getInputProps("name")}
|
||||
/>
|
||||
<ServiceTypeSegmentedControl
|
||||
value={form.values.serviceType?.toString()}
|
||||
value={form.values.serviceType}
|
||||
onChange={tab => {
|
||||
form.setFieldValue("serviceType", Number(tab));
|
||||
form.setFieldValue("services", []);
|
||||
|
||||
@ -60,7 +60,6 @@ import {
|
||||
getMarketplaces,
|
||||
getProductBarcodePdf,
|
||||
getProducts,
|
||||
getProject,
|
||||
getProjects,
|
||||
getServiceCategories,
|
||||
getServices,
|
||||
@ -85,6 +84,7 @@ import {
|
||||
updateServiceCategory,
|
||||
updateServicesKit,
|
||||
updateStatus,
|
||||
uploadProductImage,
|
||||
type Options,
|
||||
} from "../sdk.gen";
|
||||
import type {
|
||||
@ -215,7 +215,6 @@ import type {
|
||||
GetProductsData,
|
||||
GetProductsError,
|
||||
GetProductsResponse2,
|
||||
GetProjectData,
|
||||
GetProjectsData,
|
||||
GetServiceCategoriesData,
|
||||
GetServicesData,
|
||||
@ -276,6 +275,9 @@ import type {
|
||||
UpdateStatusData,
|
||||
UpdateStatusError,
|
||||
UpdateStatusResponse2,
|
||||
UploadProductImageData,
|
||||
UploadProductImageError,
|
||||
UploadProductImageResponse,
|
||||
} from "../types.gen";
|
||||
|
||||
export type QueryKey<TOptions extends Options> = [
|
||||
@ -2188,6 +2190,57 @@ export const updateProductMutation = (
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
export const uploadProductImageQueryKey = (
|
||||
options: Options<UploadProductImageData>
|
||||
) => createQueryKey("uploadProductImage", options);
|
||||
|
||||
/**
|
||||
* Upload Product Image
|
||||
*/
|
||||
export const uploadProductImageOptions = (
|
||||
options: Options<UploadProductImageData>
|
||||
) => {
|
||||
return queryOptions({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await uploadProductImage({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true,
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: uploadProductImageQueryKey(options),
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Upload Product Image
|
||||
*/
|
||||
export const uploadProductImageMutation = (
|
||||
options?: Partial<Options<UploadProductImageData>>
|
||||
): UseMutationOptions<
|
||||
UploadProductImageResponse,
|
||||
AxiosError<UploadProductImageError>,
|
||||
Options<UploadProductImageData>
|
||||
> => {
|
||||
const mutationOptions: UseMutationOptions<
|
||||
UploadProductImageResponse,
|
||||
AxiosError<UploadProductImageError>,
|
||||
Options<UploadProductImageData>
|
||||
> = {
|
||||
mutationFn: async localOptions => {
|
||||
const { data } = await uploadProductImage({
|
||||
...options,
|
||||
...localOptions,
|
||||
throwOnError: true,
|
||||
});
|
||||
return data;
|
||||
},
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
export const getProductBarcodePdfQueryKey = (
|
||||
options: Options<GetProductBarcodePdfData>
|
||||
) => createQueryKey("getProductBarcodePdf", options);
|
||||
@ -2716,27 +2769,6 @@ export const deleteProjectMutation = (
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
export const getProjectQueryKey = (options: Options<GetProjectData>) =>
|
||||
createQueryKey("getProject", options);
|
||||
|
||||
/**
|
||||
* Get Project
|
||||
*/
|
||||
export const getProjectOptions = (options: Options<GetProjectData>) => {
|
||||
return queryOptions({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await getProject({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true,
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: getProjectQueryKey(options),
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Update Project
|
||||
*/
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
// This file is auto-generated by @hey-api/openapi-ts
|
||||
|
||||
import type { Client, Options as ClientOptions, TDataShape } from "./client";
|
||||
import {
|
||||
formDataBodySerializer,
|
||||
type Client,
|
||||
type Options as ClientOptions,
|
||||
type TDataShape,
|
||||
} from "./client";
|
||||
import { client as _heyApiClient } from "./client.gen";
|
||||
import type {
|
||||
AddKitToDealData,
|
||||
@ -147,9 +152,6 @@ import type {
|
||||
GetProductsData,
|
||||
GetProductsErrors,
|
||||
GetProductsResponses,
|
||||
GetProjectData,
|
||||
GetProjectErrors,
|
||||
GetProjectResponses,
|
||||
GetProjectsData,
|
||||
GetProjectsResponses,
|
||||
GetServiceCategoriesData,
|
||||
@ -218,6 +220,9 @@ import type {
|
||||
UpdateStatusData,
|
||||
UpdateStatusErrors,
|
||||
UpdateStatusResponses,
|
||||
UploadProductImageData,
|
||||
UploadProductImageErrors,
|
||||
UploadProductImageResponses,
|
||||
} from "./types.gen";
|
||||
import {
|
||||
zAddKitToDealData,
|
||||
@ -320,8 +325,6 @@ import {
|
||||
zGetProductBarcodePdfResponse2,
|
||||
zGetProductsData,
|
||||
zGetProductsResponse2,
|
||||
zGetProjectData,
|
||||
zGetProjectResponse2,
|
||||
zGetProjectsData,
|
||||
zGetProjectsResponse2,
|
||||
zGetServiceCategoriesData,
|
||||
@ -370,6 +373,8 @@ import {
|
||||
zUpdateServicesKitResponse2,
|
||||
zUpdateStatusData,
|
||||
zUpdateStatusResponse2,
|
||||
zUploadProductImageData,
|
||||
zUploadProductImageResponse,
|
||||
} from "./zod.gen";
|
||||
|
||||
export type Options<
|
||||
@ -1695,6 +1700,34 @@ export const updateProduct = <ThrowOnError extends boolean = false>(
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Upload Product Image
|
||||
*/
|
||||
export const uploadProductImage = <ThrowOnError extends boolean = false>(
|
||||
options: Options<UploadProductImageData, ThrowOnError>
|
||||
) => {
|
||||
return (options.client ?? _heyApiClient).post<
|
||||
UploadProductImageResponses,
|
||||
UploadProductImageErrors,
|
||||
ThrowOnError
|
||||
>({
|
||||
...formDataBodySerializer,
|
||||
requestValidator: async data => {
|
||||
return await zUploadProductImageData.parseAsync(data);
|
||||
},
|
||||
responseType: "json",
|
||||
responseValidator: async data => {
|
||||
return await zUploadProductImageResponse.parseAsync(data);
|
||||
},
|
||||
url: "/crm/v1/fulfillment-base/product/images/upload/{productId}",
|
||||
...options,
|
||||
headers: {
|
||||
"Content-Type": null,
|
||||
...options.headers,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get Product Barcode Pdf
|
||||
*/
|
||||
@ -2095,29 +2128,6 @@ export const deleteProject = <ThrowOnError extends boolean = false>(
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get Project
|
||||
*/
|
||||
export const getProject = <ThrowOnError extends boolean = false>(
|
||||
options: Options<GetProjectData, ThrowOnError>
|
||||
) => {
|
||||
return (options.client ?? _heyApiClient).get<
|
||||
GetProjectResponses,
|
||||
GetProjectErrors,
|
||||
ThrowOnError
|
||||
>({
|
||||
requestValidator: async data => {
|
||||
return await zGetProjectData.parseAsync(data);
|
||||
},
|
||||
responseType: "json",
|
||||
responseValidator: async data => {
|
||||
return await zGetProjectResponse2.parseAsync(data);
|
||||
},
|
||||
url: "/crm/v1/project/{pk}",
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Update Project
|
||||
*/
|
||||
|
||||
@ -103,6 +103,16 @@ export type BoardSchema = {
|
||||
projectId: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Body_upload_product_image
|
||||
*/
|
||||
export type BodyUploadProductImage = {
|
||||
/**
|
||||
* Upload File
|
||||
*/
|
||||
upload_file: Blob | File;
|
||||
};
|
||||
|
||||
/**
|
||||
* BuiltInModuleSchema
|
||||
*/
|
||||
@ -630,6 +640,14 @@ export type CreateProductSchema = {
|
||||
* Barcodes
|
||||
*/
|
||||
barcodes: Array<string>;
|
||||
/**
|
||||
* Imageurl
|
||||
*/
|
||||
imageUrl?: string | null;
|
||||
/**
|
||||
* Images
|
||||
*/
|
||||
images?: Array<ProductImageSchema> | null;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1400,13 +1418,6 @@ export type GetProductsResponse = {
|
||||
paginationInfo: PaginationInfoSchema;
|
||||
};
|
||||
|
||||
/**
|
||||
* GetProjectResponse
|
||||
*/
|
||||
export type GetProjectResponse = {
|
||||
entity: ProjectSchema;
|
||||
};
|
||||
|
||||
/**
|
||||
* GetProjectsResponse
|
||||
*/
|
||||
@ -1593,6 +1604,14 @@ export type ProductSchema = {
|
||||
* Barcodes
|
||||
*/
|
||||
barcodes: Array<string>;
|
||||
/**
|
||||
* Imageurl
|
||||
*/
|
||||
imageUrl?: string | null;
|
||||
/**
|
||||
* Images
|
||||
*/
|
||||
images?: Array<ProductImageSchema> | null;
|
||||
/**
|
||||
* Id
|
||||
*/
|
||||
@ -1655,6 +1674,20 @@ export type ProductServicesDuplicateResponse = {
|
||||
message: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* ProductUploadImageResponse
|
||||
*/
|
||||
export type ProductUploadImageResponse = {
|
||||
/**
|
||||
* Message
|
||||
*/
|
||||
message: string;
|
||||
/**
|
||||
* Imageurl
|
||||
*/
|
||||
imageUrl?: string | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* ProjectSchema
|
||||
*/
|
||||
@ -4036,6 +4069,38 @@ export type UpdateProductResponses = {
|
||||
export type UpdateProductResponse2 =
|
||||
UpdateProductResponses[keyof UpdateProductResponses];
|
||||
|
||||
export type UploadProductImageData = {
|
||||
body: BodyUploadProductImage;
|
||||
path: {
|
||||
/**
|
||||
* Productid
|
||||
*/
|
||||
productId: number;
|
||||
};
|
||||
query?: never;
|
||||
url: "/crm/v1/fulfillment-base/product/images/upload/{productId}";
|
||||
};
|
||||
|
||||
export type UploadProductImageErrors = {
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
422: HttpValidationError;
|
||||
};
|
||||
|
||||
export type UploadProductImageError =
|
||||
UploadProductImageErrors[keyof UploadProductImageErrors];
|
||||
|
||||
export type UploadProductImageResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: ProductUploadImageResponse;
|
||||
};
|
||||
|
||||
export type UploadProductImageResponse =
|
||||
UploadProductImageResponses[keyof UploadProductImageResponses];
|
||||
|
||||
export type GetProductBarcodePdfData = {
|
||||
body: GetProductBarcodePdfRequest;
|
||||
path?: never;
|
||||
@ -4458,37 +4523,6 @@ export type DeleteProjectResponses = {
|
||||
export type DeleteProjectResponse2 =
|
||||
DeleteProjectResponses[keyof DeleteProjectResponses];
|
||||
|
||||
export type GetProjectData = {
|
||||
body?: never;
|
||||
path: {
|
||||
/**
|
||||
* Pk
|
||||
*/
|
||||
pk: number;
|
||||
};
|
||||
query?: never;
|
||||
url: "/crm/v1/project/{pk}";
|
||||
};
|
||||
|
||||
export type GetProjectErrors = {
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
422: HttpValidationError;
|
||||
};
|
||||
|
||||
export type GetProjectError = GetProjectErrors[keyof GetProjectErrors];
|
||||
|
||||
export type GetProjectResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: GetProjectResponse;
|
||||
};
|
||||
|
||||
export type GetProjectResponse2 =
|
||||
GetProjectResponses[keyof GetProjectResponses];
|
||||
|
||||
export type UpdateProjectData = {
|
||||
body: UpdateProjectRequest;
|
||||
path: {
|
||||
|
||||
@ -51,6 +51,13 @@ export const zBoardSchema = z.object({
|
||||
projectId: z.int(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Body_upload_product_image
|
||||
*/
|
||||
export const zBodyUploadProductImage = z.object({
|
||||
upload_file: z.any(),
|
||||
});
|
||||
|
||||
/**
|
||||
* BuiltInModuleTabSchema
|
||||
*/
|
||||
@ -234,6 +241,15 @@ export const zCreateDealProductRequest = z.object({
|
||||
entity: zCreateDealProductSchema,
|
||||
});
|
||||
|
||||
/**
|
||||
* ProductImageSchema
|
||||
*/
|
||||
export const zProductImageSchema = z.object({
|
||||
id: z.int(),
|
||||
productId: z.int(),
|
||||
imageUrl: z.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
* ProductSchema
|
||||
*/
|
||||
@ -249,6 +265,8 @@ export const zProductSchema = z.object({
|
||||
size: z.union([z.string(), z.null()]),
|
||||
additionalInfo: z.union([z.string(), z.null()]),
|
||||
barcodes: z.array(z.string()),
|
||||
imageUrl: z.optional(z.union([z.string(), z.null()])),
|
||||
images: z.optional(z.union([z.array(zProductImageSchema), z.null()])),
|
||||
id: z.int(),
|
||||
barcodeTemplate: zBarcodeTemplateSchema,
|
||||
});
|
||||
@ -509,6 +527,8 @@ export const zCreateProductSchema = z.object({
|
||||
size: z.union([z.string(), z.null()]),
|
||||
additionalInfo: z.union([z.string(), z.null()]),
|
||||
barcodes: z.array(z.string()),
|
||||
imageUrl: z.optional(z.union([z.string(), z.null()])),
|
||||
images: z.optional(z.union([z.array(zProductImageSchema), z.null()])),
|
||||
});
|
||||
|
||||
/**
|
||||
@ -957,13 +977,6 @@ export const zGetProductsResponse = z.object({
|
||||
paginationInfo: zPaginationInfoSchema,
|
||||
});
|
||||
|
||||
/**
|
||||
* GetProjectResponse
|
||||
*/
|
||||
export const zGetProjectResponse = z.object({
|
||||
entity: zProjectSchema,
|
||||
});
|
||||
|
||||
/**
|
||||
* GetProjectsResponse
|
||||
*/
|
||||
@ -1042,15 +1055,6 @@ export const zHttpValidationError = z.object({
|
||||
detail: z.optional(z.array(zValidationError)),
|
||||
});
|
||||
|
||||
/**
|
||||
* ProductImageSchema
|
||||
*/
|
||||
export const zProductImageSchema = z.object({
|
||||
id: z.int(),
|
||||
productId: z.int(),
|
||||
imageUrl: z.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
* ProductServicesDuplicateRequest
|
||||
*/
|
||||
@ -1067,6 +1071,14 @@ export const zProductServicesDuplicateResponse = z.object({
|
||||
message: z.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
* ProductUploadImageResponse
|
||||
*/
|
||||
export const zProductUploadImageResponse = z.object({
|
||||
message: z.string(),
|
||||
imageUrl: z.optional(z.union([z.string(), z.null()])),
|
||||
});
|
||||
|
||||
export const zSortDir = z.enum(["asc", "desc"]);
|
||||
|
||||
/**
|
||||
@ -2146,6 +2158,19 @@ export const zUpdateProductData = z.object({
|
||||
*/
|
||||
export const zUpdateProductResponse2 = zUpdateProductResponse;
|
||||
|
||||
export const zUploadProductImageData = z.object({
|
||||
body: zBodyUploadProductImage,
|
||||
path: z.object({
|
||||
productId: z.int(),
|
||||
}),
|
||||
query: z.optional(z.never()),
|
||||
});
|
||||
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
export const zUploadProductImageResponse = zProductUploadImageResponse;
|
||||
|
||||
export const zGetProductBarcodePdfData = z.object({
|
||||
body: zGetProductBarcodePdfRequest,
|
||||
path: z.optional(z.never()),
|
||||
@ -2336,19 +2361,6 @@ export const zDeleteProjectData = z.object({
|
||||
*/
|
||||
export const zDeleteProjectResponse2 = zDeleteProjectResponse;
|
||||
|
||||
export const zGetProjectData = z.object({
|
||||
body: z.optional(z.never()),
|
||||
path: z.object({
|
||||
pk: z.int(),
|
||||
}),
|
||||
query: z.optional(z.never()),
|
||||
});
|
||||
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
export const zGetProjectResponse2 = zGetProjectResponse;
|
||||
|
||||
export const zUpdateProjectData = z.object({
|
||||
body: zUpdateProjectRequest,
|
||||
path: z.object({
|
||||
|
||||
@ -25,14 +25,16 @@ export type BaseFormProps<T> = {
|
||||
onClose: () => void;
|
||||
closeOnSubmit?: boolean;
|
||||
children: ReactNode;
|
||||
actionsEnabled?: boolean;
|
||||
};
|
||||
|
||||
type Props<TCreate, TUpdate, TEntity> = BaseFormProps<TEntity> &
|
||||
CreateEditFormProps<TCreate, TUpdate, TEntity>;
|
||||
|
||||
const BaseFormModal = <TCreate, TUpdate = TCreate, TEntity = TUpdate>(
|
||||
props: Props<TCreate, TUpdate, TEntity>
|
||||
) => {
|
||||
const BaseFormModal = <TCreate, TUpdate = TCreate, TEntity = TUpdate>({
|
||||
actionsEnabled = true,
|
||||
...props
|
||||
}: Props<TCreate, TUpdate, TEntity>) => {
|
||||
const { closeOnSubmit = false } = props;
|
||||
|
||||
const onSubmit = (values: Partial<TEntity>) => {
|
||||
@ -54,7 +56,7 @@ const BaseFormModal = <TCreate, TUpdate = TCreate, TEntity = TUpdate>(
|
||||
gap={"xs"}
|
||||
direction={"column"}>
|
||||
{props.children}
|
||||
<BaseFormModalActions {...props} />
|
||||
{actionsEnabled && <BaseFormModalActions {...props} />}
|
||||
</Flex>
|
||||
</form>
|
||||
);
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -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;
|
||||
Reference in New Issue
Block a user