feat: product images upload and display
This commit is contained in:
10
fix-zod.ts
Normal file
10
fix-zod.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import * as fs from "fs";
|
||||||
|
|
||||||
|
const path = "src/lib/client/zod.gen.ts";
|
||||||
|
let content = fs.readFileSync(path, "utf8");
|
||||||
|
|
||||||
|
// Replace only for the upload schema
|
||||||
|
content = content.replace("upload_file: z.string", "upload_file: z.any");
|
||||||
|
|
||||||
|
fs.writeFileSync(path, content);
|
||||||
|
console.log("✅ Fixed zod schema for upload_file");
|
||||||
@ -7,7 +7,7 @@
|
|||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"generate-client": "openapi-ts && prettier --write ./src/lib/client/**/*.ts && git add ./src/lib/client",
|
"generate-client": "openapi-ts && prettier --write ./src/lib/client/**/*.ts && git add ./src/lib/client & sudo npx tsc ./fix-zod.ts && mv -f ./fix-zod.js ./fix-zod.cjs && sudo node ./fix-zod.cjs",
|
||||||
"generate-modules": "sudo npx tsc ./src/modules/modulesFileGen/modulesFileGen.ts && mv -f ./src/modules/modulesFileGen/modulesFileGen.js ./src/modules/modulesFileGen/modulesFileGen.cjs && sudo node ./src/modules/modulesFileGen/modulesFileGen.cjs"
|
"generate-modules": "sudo npx tsc ./src/modules/modulesFileGen/modulesFileGen.ts && mv -f ./src/modules/modulesFileGen/modulesFileGen.js ./src/modules/modulesFileGen/modulesFileGen.cjs && sudo node ./src/modules/modulesFileGen/modulesFileGen.cjs"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@ -47,7 +47,7 @@ const ServicesKitEditorModal = ({
|
|||||||
{...form.getInputProps("name")}
|
{...form.getInputProps("name")}
|
||||||
/>
|
/>
|
||||||
<ServiceTypeSegmentedControl
|
<ServiceTypeSegmentedControl
|
||||||
value={form.values.serviceType?.toString()}
|
value={form.values.serviceType}
|
||||||
onChange={tab => {
|
onChange={tab => {
|
||||||
form.setFieldValue("serviceType", Number(tab));
|
form.setFieldValue("serviceType", Number(tab));
|
||||||
form.setFieldValue("services", []);
|
form.setFieldValue("services", []);
|
||||||
|
|||||||
@ -60,7 +60,6 @@ import {
|
|||||||
getMarketplaces,
|
getMarketplaces,
|
||||||
getProductBarcodePdf,
|
getProductBarcodePdf,
|
||||||
getProducts,
|
getProducts,
|
||||||
getProject,
|
|
||||||
getProjects,
|
getProjects,
|
||||||
getServiceCategories,
|
getServiceCategories,
|
||||||
getServices,
|
getServices,
|
||||||
@ -85,6 +84,7 @@ import {
|
|||||||
updateServiceCategory,
|
updateServiceCategory,
|
||||||
updateServicesKit,
|
updateServicesKit,
|
||||||
updateStatus,
|
updateStatus,
|
||||||
|
uploadProductImage,
|
||||||
type Options,
|
type Options,
|
||||||
} from "../sdk.gen";
|
} from "../sdk.gen";
|
||||||
import type {
|
import type {
|
||||||
@ -215,7 +215,6 @@ import type {
|
|||||||
GetProductsData,
|
GetProductsData,
|
||||||
GetProductsError,
|
GetProductsError,
|
||||||
GetProductsResponse2,
|
GetProductsResponse2,
|
||||||
GetProjectData,
|
|
||||||
GetProjectsData,
|
GetProjectsData,
|
||||||
GetServiceCategoriesData,
|
GetServiceCategoriesData,
|
||||||
GetServicesData,
|
GetServicesData,
|
||||||
@ -276,6 +275,9 @@ import type {
|
|||||||
UpdateStatusData,
|
UpdateStatusData,
|
||||||
UpdateStatusError,
|
UpdateStatusError,
|
||||||
UpdateStatusResponse2,
|
UpdateStatusResponse2,
|
||||||
|
UploadProductImageData,
|
||||||
|
UploadProductImageError,
|
||||||
|
UploadProductImageResponse,
|
||||||
} from "../types.gen";
|
} from "../types.gen";
|
||||||
|
|
||||||
export type QueryKey<TOptions extends Options> = [
|
export type QueryKey<TOptions extends Options> = [
|
||||||
@ -2188,6 +2190,57 @@ export const updateProductMutation = (
|
|||||||
return mutationOptions;
|
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 = (
|
export const getProductBarcodePdfQueryKey = (
|
||||||
options: Options<GetProductBarcodePdfData>
|
options: Options<GetProductBarcodePdfData>
|
||||||
) => createQueryKey("getProductBarcodePdf", options);
|
) => createQueryKey("getProductBarcodePdf", options);
|
||||||
@ -2716,27 +2769,6 @@ export const deleteProjectMutation = (
|
|||||||
return mutationOptions;
|
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
|
* Update Project
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -1,6 +1,11 @@
|
|||||||
// This file is auto-generated by @hey-api/openapi-ts
|
// 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 { client as _heyApiClient } from "./client.gen";
|
||||||
import type {
|
import type {
|
||||||
AddKitToDealData,
|
AddKitToDealData,
|
||||||
@ -147,9 +152,6 @@ import type {
|
|||||||
GetProductsData,
|
GetProductsData,
|
||||||
GetProductsErrors,
|
GetProductsErrors,
|
||||||
GetProductsResponses,
|
GetProductsResponses,
|
||||||
GetProjectData,
|
|
||||||
GetProjectErrors,
|
|
||||||
GetProjectResponses,
|
|
||||||
GetProjectsData,
|
GetProjectsData,
|
||||||
GetProjectsResponses,
|
GetProjectsResponses,
|
||||||
GetServiceCategoriesData,
|
GetServiceCategoriesData,
|
||||||
@ -218,6 +220,9 @@ import type {
|
|||||||
UpdateStatusData,
|
UpdateStatusData,
|
||||||
UpdateStatusErrors,
|
UpdateStatusErrors,
|
||||||
UpdateStatusResponses,
|
UpdateStatusResponses,
|
||||||
|
UploadProductImageData,
|
||||||
|
UploadProductImageErrors,
|
||||||
|
UploadProductImageResponses,
|
||||||
} from "./types.gen";
|
} from "./types.gen";
|
||||||
import {
|
import {
|
||||||
zAddKitToDealData,
|
zAddKitToDealData,
|
||||||
@ -320,8 +325,6 @@ import {
|
|||||||
zGetProductBarcodePdfResponse2,
|
zGetProductBarcodePdfResponse2,
|
||||||
zGetProductsData,
|
zGetProductsData,
|
||||||
zGetProductsResponse2,
|
zGetProductsResponse2,
|
||||||
zGetProjectData,
|
|
||||||
zGetProjectResponse2,
|
|
||||||
zGetProjectsData,
|
zGetProjectsData,
|
||||||
zGetProjectsResponse2,
|
zGetProjectsResponse2,
|
||||||
zGetServiceCategoriesData,
|
zGetServiceCategoriesData,
|
||||||
@ -370,6 +373,8 @@ import {
|
|||||||
zUpdateServicesKitResponse2,
|
zUpdateServicesKitResponse2,
|
||||||
zUpdateStatusData,
|
zUpdateStatusData,
|
||||||
zUpdateStatusResponse2,
|
zUpdateStatusResponse2,
|
||||||
|
zUploadProductImageData,
|
||||||
|
zUploadProductImageResponse,
|
||||||
} from "./zod.gen";
|
} from "./zod.gen";
|
||||||
|
|
||||||
export type Options<
|
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
|
* 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
|
* Update Project
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -103,6 +103,16 @@ export type BoardSchema = {
|
|||||||
projectId: number;
|
projectId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Body_upload_product_image
|
||||||
|
*/
|
||||||
|
export type BodyUploadProductImage = {
|
||||||
|
/**
|
||||||
|
* Upload File
|
||||||
|
*/
|
||||||
|
upload_file: Blob | File;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BuiltInModuleSchema
|
* BuiltInModuleSchema
|
||||||
*/
|
*/
|
||||||
@ -630,6 +640,14 @@ export type CreateProductSchema = {
|
|||||||
* Barcodes
|
* Barcodes
|
||||||
*/
|
*/
|
||||||
barcodes: Array<string>;
|
barcodes: Array<string>;
|
||||||
|
/**
|
||||||
|
* Imageurl
|
||||||
|
*/
|
||||||
|
imageUrl?: string | null;
|
||||||
|
/**
|
||||||
|
* Images
|
||||||
|
*/
|
||||||
|
images?: Array<ProductImageSchema> | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1400,13 +1418,6 @@ export type GetProductsResponse = {
|
|||||||
paginationInfo: PaginationInfoSchema;
|
paginationInfo: PaginationInfoSchema;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* GetProjectResponse
|
|
||||||
*/
|
|
||||||
export type GetProjectResponse = {
|
|
||||||
entity: ProjectSchema;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GetProjectsResponse
|
* GetProjectsResponse
|
||||||
*/
|
*/
|
||||||
@ -1593,6 +1604,14 @@ export type ProductSchema = {
|
|||||||
* Barcodes
|
* Barcodes
|
||||||
*/
|
*/
|
||||||
barcodes: Array<string>;
|
barcodes: Array<string>;
|
||||||
|
/**
|
||||||
|
* Imageurl
|
||||||
|
*/
|
||||||
|
imageUrl?: string | null;
|
||||||
|
/**
|
||||||
|
* Images
|
||||||
|
*/
|
||||||
|
images?: Array<ProductImageSchema> | null;
|
||||||
/**
|
/**
|
||||||
* Id
|
* Id
|
||||||
*/
|
*/
|
||||||
@ -1655,6 +1674,20 @@ export type ProductServicesDuplicateResponse = {
|
|||||||
message: string;
|
message: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProductUploadImageResponse
|
||||||
|
*/
|
||||||
|
export type ProductUploadImageResponse = {
|
||||||
|
/**
|
||||||
|
* Message
|
||||||
|
*/
|
||||||
|
message: string;
|
||||||
|
/**
|
||||||
|
* Imageurl
|
||||||
|
*/
|
||||||
|
imageUrl?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ProjectSchema
|
* ProjectSchema
|
||||||
*/
|
*/
|
||||||
@ -4036,6 +4069,38 @@ export type UpdateProductResponses = {
|
|||||||
export type UpdateProductResponse2 =
|
export type UpdateProductResponse2 =
|
||||||
UpdateProductResponses[keyof UpdateProductResponses];
|
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 = {
|
export type GetProductBarcodePdfData = {
|
||||||
body: GetProductBarcodePdfRequest;
|
body: GetProductBarcodePdfRequest;
|
||||||
path?: never;
|
path?: never;
|
||||||
@ -4458,37 +4523,6 @@ export type DeleteProjectResponses = {
|
|||||||
export type DeleteProjectResponse2 =
|
export type DeleteProjectResponse2 =
|
||||||
DeleteProjectResponses[keyof DeleteProjectResponses];
|
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 = {
|
export type UpdateProjectData = {
|
||||||
body: UpdateProjectRequest;
|
body: UpdateProjectRequest;
|
||||||
path: {
|
path: {
|
||||||
|
|||||||
@ -51,6 +51,13 @@ export const zBoardSchema = z.object({
|
|||||||
projectId: z.int(),
|
projectId: z.int(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Body_upload_product_image
|
||||||
|
*/
|
||||||
|
export const zBodyUploadProductImage = z.object({
|
||||||
|
upload_file: z.any(),
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BuiltInModuleTabSchema
|
* BuiltInModuleTabSchema
|
||||||
*/
|
*/
|
||||||
@ -234,6 +241,15 @@ export const zCreateDealProductRequest = z.object({
|
|||||||
entity: zCreateDealProductSchema,
|
entity: zCreateDealProductSchema,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProductImageSchema
|
||||||
|
*/
|
||||||
|
export const zProductImageSchema = z.object({
|
||||||
|
id: z.int(),
|
||||||
|
productId: z.int(),
|
||||||
|
imageUrl: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ProductSchema
|
* ProductSchema
|
||||||
*/
|
*/
|
||||||
@ -249,6 +265,8 @@ export const zProductSchema = z.object({
|
|||||||
size: z.union([z.string(), z.null()]),
|
size: z.union([z.string(), z.null()]),
|
||||||
additionalInfo: z.union([z.string(), z.null()]),
|
additionalInfo: z.union([z.string(), z.null()]),
|
||||||
barcodes: z.array(z.string()),
|
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(),
|
id: z.int(),
|
||||||
barcodeTemplate: zBarcodeTemplateSchema,
|
barcodeTemplate: zBarcodeTemplateSchema,
|
||||||
});
|
});
|
||||||
@ -509,6 +527,8 @@ export const zCreateProductSchema = z.object({
|
|||||||
size: z.union([z.string(), z.null()]),
|
size: z.union([z.string(), z.null()]),
|
||||||
additionalInfo: z.union([z.string(), z.null()]),
|
additionalInfo: z.union([z.string(), z.null()]),
|
||||||
barcodes: z.array(z.string()),
|
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,
|
paginationInfo: zPaginationInfoSchema,
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* GetProjectResponse
|
|
||||||
*/
|
|
||||||
export const zGetProjectResponse = z.object({
|
|
||||||
entity: zProjectSchema,
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GetProjectsResponse
|
* GetProjectsResponse
|
||||||
*/
|
*/
|
||||||
@ -1042,15 +1055,6 @@ export const zHttpValidationError = z.object({
|
|||||||
detail: z.optional(z.array(zValidationError)),
|
detail: z.optional(z.array(zValidationError)),
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* ProductImageSchema
|
|
||||||
*/
|
|
||||||
export const zProductImageSchema = z.object({
|
|
||||||
id: z.int(),
|
|
||||||
productId: z.int(),
|
|
||||||
imageUrl: z.string(),
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ProductServicesDuplicateRequest
|
* ProductServicesDuplicateRequest
|
||||||
*/
|
*/
|
||||||
@ -1067,6 +1071,14 @@ export const zProductServicesDuplicateResponse = z.object({
|
|||||||
message: z.string(),
|
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"]);
|
export const zSortDir = z.enum(["asc", "desc"]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2146,6 +2158,19 @@ export const zUpdateProductData = z.object({
|
|||||||
*/
|
*/
|
||||||
export const zUpdateProductResponse2 = zUpdateProductResponse;
|
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({
|
export const zGetProductBarcodePdfData = z.object({
|
||||||
body: zGetProductBarcodePdfRequest,
|
body: zGetProductBarcodePdfRequest,
|
||||||
path: z.optional(z.never()),
|
path: z.optional(z.never()),
|
||||||
@ -2336,19 +2361,6 @@ export const zDeleteProjectData = z.object({
|
|||||||
*/
|
*/
|
||||||
export const zDeleteProjectResponse2 = zDeleteProjectResponse;
|
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({
|
export const zUpdateProjectData = z.object({
|
||||||
body: zUpdateProjectRequest,
|
body: zUpdateProjectRequest,
|
||||||
path: z.object({
|
path: z.object({
|
||||||
|
|||||||
@ -25,14 +25,16 @@ export type BaseFormProps<T> = {
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
closeOnSubmit?: boolean;
|
closeOnSubmit?: boolean;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
|
actionsEnabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props<TCreate, TUpdate, TEntity> = BaseFormProps<TEntity> &
|
type Props<TCreate, TUpdate, TEntity> = BaseFormProps<TEntity> &
|
||||||
CreateEditFormProps<TCreate, TUpdate, TEntity>;
|
CreateEditFormProps<TCreate, TUpdate, TEntity>;
|
||||||
|
|
||||||
const BaseFormModal = <TCreate, TUpdate = TCreate, TEntity = TUpdate>(
|
const BaseFormModal = <TCreate, TUpdate = TCreate, TEntity = TUpdate>({
|
||||||
props: Props<TCreate, TUpdate, TEntity>
|
actionsEnabled = true,
|
||||||
) => {
|
...props
|
||||||
|
}: Props<TCreate, TUpdate, TEntity>) => {
|
||||||
const { closeOnSubmit = false } = props;
|
const { closeOnSubmit = false } = props;
|
||||||
|
|
||||||
const onSubmit = (values: Partial<TEntity>) => {
|
const onSubmit = (values: Partial<TEntity>) => {
|
||||||
@ -54,7 +56,7 @@ const BaseFormModal = <TCreate, TUpdate = TCreate, TEntity = TUpdate>(
|
|||||||
gap={"xs"}
|
gap={"xs"}
|
||||||
direction={"column"}>
|
direction={"column"}>
|
||||||
{props.children}
|
{props.children}
|
||||||
<BaseFormModalActions {...props} />
|
{actionsEnabled && <BaseFormModalActions {...props} />}
|
||||||
</Flex>
|
</Flex>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { DropzoneProps, FileWithPath } from "@mantine/dropzone";
|
import { DropzoneProps, FileWithPath } from "@mantine/dropzone";
|
||||||
import ImageDropzone from "@/components/ui/ImageDropzone/ImageDropzone";
|
import ImageDropzone from "@/components/ui/ImageDropzone/ImageDropzone";
|
||||||
|
import { uploadProductImage } from "@/lib/client";
|
||||||
import { notifications } from "@/lib/notifications";
|
import { notifications } from "@/lib/notifications";
|
||||||
import BaseFormInputProps from "@/utils/baseFormInputProps";
|
import BaseFormInputProps from "@/utils/baseFormInputProps";
|
||||||
import useImageDropzone from "./useImageDropzone";
|
import useImageDropzone from "./useImageDropzone";
|
||||||
@ -31,30 +32,31 @@ const ProductImageDropzone: FC<Props> = ({
|
|||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
// TODO SEND REQUEST
|
uploadProductImage({
|
||||||
|
path: {
|
||||||
|
productId,
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
upload_file: file,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
notifications.success({ message: data?.message });
|
||||||
|
setIsLoading(false);
|
||||||
|
|
||||||
// ProductService.uploadProductImage({
|
if (!data?.imageUrl) {
|
||||||
// productId,
|
setShowDropzone(true);
|
||||||
// formData: {
|
return;
|
||||||
// upload_file: file,
|
}
|
||||||
// },
|
imageUrlInputProps?.onChange(data?.imageUrl);
|
||||||
// })
|
setShowDropzone(false);
|
||||||
// .then(({ ok, message, imageUrl }) => {
|
})
|
||||||
// notifications.guess(ok, { message });
|
.catch(err => {
|
||||||
// setIsLoading(false);
|
console.log(err);
|
||||||
//
|
notifications.error({ message: err.toString() });
|
||||||
// if (!ok || !imageUrl) {
|
setShowDropzone(true);
|
||||||
// setShowDropzone(true);
|
setIsLoading(false);
|
||||||
// return;
|
});
|
||||||
// }
|
|
||||||
// imageUrlInputProps?.onChange(imageUrl);
|
|
||||||
// setShowDropzone(false);
|
|
||||||
// })
|
|
||||||
// .catch(error => {
|
|
||||||
// notifications.error({ message: error.toString() });
|
|
||||||
// setShowDropzone(true);
|
|
||||||
// setIsLoading(false);
|
|
||||||
// });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -107,12 +107,12 @@ const ProductView: FC<Props> = ({ dealProduct }) => {
|
|||||||
<Stack
|
<Stack
|
||||||
flex={2}
|
flex={2}
|
||||||
gap={"sm"}>
|
gap={"sm"}>
|
||||||
{!dealProduct.product && (
|
{dealProduct.product?.imageUrl && (
|
||||||
<Image
|
<Image
|
||||||
flex={1}
|
flex={1}
|
||||||
radius={"md"}
|
radius={"md"}
|
||||||
fit={"cover"}
|
fit={"cover"}
|
||||||
// src={dealProduct.product.imageUrl}
|
src={dealProduct.product.imageUrl}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Title order={3}>{dealProduct.product.name}</Title>
|
<Title order={3}>{dealProduct.product.name}</Title>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
import { Fieldset, Flex, Stack, TagsInput, TextInput } from "@mantine/core";
|
import { Fieldset, Flex, Stack, TagsInput, TextInput } from "@mantine/core";
|
||||||
import { useForm } from "@mantine/form";
|
import { useForm } from "@mantine/form";
|
||||||
import { ContextModalProps } from "@mantine/modals";
|
import { ContextModalProps } from "@mantine/modals";
|
||||||
@ -14,6 +15,9 @@ import BaseFormModal, {
|
|||||||
CreateEditFormProps,
|
CreateEditFormProps,
|
||||||
} from "@/modals/base/BaseFormModal/BaseFormModal";
|
} from "@/modals/base/BaseFormModal/BaseFormModal";
|
||||||
import ProductImageDropzone from "@/modules/dealModularEditorTabs/FulfillmentBase/desktop/components/ProductImageDropzone/ProductImageDropzone";
|
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";
|
import BaseFormInputProps from "@/utils/baseFormInputProps";
|
||||||
|
|
||||||
type Props = CreateEditFormProps<
|
type Props = CreateEditFormProps<
|
||||||
@ -35,6 +39,9 @@ const ProductEditorModal = ({
|
|||||||
id,
|
id,
|
||||||
innerProps,
|
innerProps,
|
||||||
}: ContextModalProps<Props>) => {
|
}: ContextModalProps<Props>) => {
|
||||||
|
const [editorTab, setEditorTab] = useState<ProductEditorTab>(
|
||||||
|
ProductEditorTab.CHARACTERISTICS
|
||||||
|
);
|
||||||
const isEditing = "entity" in innerProps;
|
const isEditing = "entity" in innerProps;
|
||||||
|
|
||||||
const initialValues: ProductForm = isEditing
|
const initialValues: ProductForm = isEditing
|
||||||
@ -63,15 +70,8 @@ const ProductEditorModal = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
const characteristicsTab = (
|
||||||
<BaseFormModal
|
<>
|
||||||
{...innerProps}
|
|
||||||
form={form}
|
|
||||||
closeOnSubmit
|
|
||||||
onClose={() => context.closeContextModal(id)}>
|
|
||||||
<Flex
|
|
||||||
gap={"xs"}
|
|
||||||
direction={"column"}>
|
|
||||||
<Fieldset legend={"Основные характеристики"}>
|
<Fieldset legend={"Основные характеристики"}>
|
||||||
<Stack gap={"xs"}>
|
<Stack gap={"xs"}>
|
||||||
<TextInput
|
<TextInput
|
||||||
@ -141,16 +141,37 @@ const ProductEditorModal = ({
|
|||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Fieldset>
|
</Fieldset>
|
||||||
{isEditing && (
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const imageTab = isEditing && (
|
||||||
<ProductImageDropzone
|
<ProductImageDropzone
|
||||||
imageUrlInputProps={
|
imageUrlInputProps={
|
||||||
form.getInputProps(
|
form.getInputProps("imageUrl") as BaseFormInputProps<string>
|
||||||
"imageUrl"
|
|
||||||
) as BaseFormInputProps<string>
|
|
||||||
}
|
}
|
||||||
productId={innerProps.entity.id}
|
productId={innerProps.entity.id}
|
||||||
/>
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseFormModal
|
||||||
|
{...innerProps}
|
||||||
|
form={form}
|
||||||
|
closeOnSubmit
|
||||||
|
actionsEnabled={editorTab === ProductEditorTab.CHARACTERISTICS}
|
||||||
|
onClose={() => context.closeContextModal(id)}>
|
||||||
|
<Flex
|
||||||
|
gap={"xs"}
|
||||||
|
direction={"column"}>
|
||||||
|
{isEditing && (
|
||||||
|
<ProductEditorSegmentedControl
|
||||||
|
value={editorTab}
|
||||||
|
onChange={setEditorTab}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
{editorTab === ProductEditorTab.CHARACTERISTICS
|
||||||
|
? characteristicsTab
|
||||||
|
: imageTab}
|
||||||
</Flex>
|
</Flex>
|
||||||
</BaseFormModal>
|
</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