diff --git a/server/src/honoMiddlewares/baseMiddleware.ts b/server/src/honoMiddlewares/baseMiddleware.ts index a501e27ac..7c88e6b0a 100644 --- a/server/src/honoMiddlewares/baseMiddleware.ts +++ b/server/src/honoMiddlewares/baseMiddleware.ts @@ -66,13 +66,11 @@ export const baseMiddleware = async (c: Context, next: Next) => { body = await c.req.json(); } - logger.info(`${method} ${path}`, { + logger.info(`[HONO] ${method} ${path}`, { context: { body, }, }); - logger.info(`URL: ${c.req.url}`); - await next(); }; diff --git a/server/src/internal/features/handlers/handleCreateFeature.ts b/server/src/internal/features/handlers/handleCreateFeature.ts index 6578359d1..6bf64bebe 100644 --- a/server/src/internal/features/handlers/handleCreateFeature.ts +++ b/server/src/internal/features/handlers/handleCreateFeature.ts @@ -1,18 +1,18 @@ -import { Feature } from "@autumn/shared"; -import { validateFeature } from "../internalFeatureRouter.js"; -import { generateId } from "@/utils/genUtils.js"; +import type { Feature } from "@autumn/shared"; import { OrgService } from "@/internal/orgs/OrgService.js"; -import { FeatureService } from "../FeatureService.js"; import { JobName } from "@/queue/JobName.js"; import { addTaskToQueue } from "@/queue/queueUtils.js"; import { handleFrontendReqError } from "@/utils/errorUtils.js"; +import { generateId } from "@/utils/genUtils.js"; +import { FeatureService } from "../FeatureService.js"; +import { validateFeature } from "../internalFeatureRouter.js"; export const handleCreateFeature = async (req: any, res: any) => { try { console.log("Trying to create feature"); const data = req.body; - let { db, orgId, env, logtail: logger } = req; - let parsedFeature = validateFeature(data); + const { db, orgId, env, logtail: logger } = req; + const parsedFeature = validateFeature(data); const feature: Feature = { archived: false, @@ -23,8 +23,8 @@ export const handleCreateFeature = async (req: any, res: any) => { ...parsedFeature, }; - let org = await OrgService.getFromReq(req); - let insertedData = await FeatureService.insert({ + const org = await OrgService.getFromReq(req); + const insertedData = await FeatureService.insert({ db, data: feature, logger, @@ -38,7 +38,7 @@ export const handleCreateFeature = async (req: any, res: any) => { }, }); - let insertedFeature = + const insertedFeature = insertedData && insertedData.length > 0 ? insertedData[0] : null; res.status(200).json(insertedFeature); } catch (error) { diff --git a/server/src/internal/features/internalFeatureRouter.ts b/server/src/internal/features/internalFeatureRouter.ts index 24c81f401..902e7e22c 100644 --- a/server/src/internal/features/internalFeatureRouter.ts +++ b/server/src/internal/features/internalFeatureRouter.ts @@ -1,15 +1,15 @@ -import express, { Router } from "express"; - -import { FeatureService } from "./FeatureService.js"; -import { ErrCode, FeatureType, products, entitlements } from "@autumn/shared"; -import { validateCreditSystem, validateFeatureId } from "./featureUtils.js"; -import { generateId } from "@/utils/genUtils.js"; -import { handleUpdateFeature } from "@/internal/features/handlers/handleUpdateFeature.js"; +import { CreateFeatureSchema, ErrCode, FeatureType } from "@autumn/shared"; +import express, { type Router } from "express"; import { handleDeleteFeature } from "@/internal/features/handlers/handleDeleteFeature.js"; +import { handleUpdateFeature } from "@/internal/features/handlers/handleUpdateFeature.js"; import RecaseError, { formatZodError } from "@/utils/errorUtils.js"; -import { validateMeteredConfig } from "./featureUtils.js"; -import { CreateFeatureSchema } from "@autumn/shared"; -import { sql, eq, and } from "drizzle-orm"; +import { generateId } from "@/utils/genUtils.js"; +import { FeatureService } from "./FeatureService.js"; +import { + validateCreditSystem, + validateFeatureId, + validateMeteredConfig, +} from "./featureUtils.js"; import { handleCreateFeature } from "./handlers/handleCreateFeature.js"; import { handleGetFeatureDeletionInfo } from "./handlers/handleGetFeatureDeletionInfo.js"; @@ -17,20 +17,20 @@ export const internalFeatureRouter: Router = express.Router(); internalFeatureRouter.get("", async (req: any, res: any) => { try { - let { showArchived } = req.query; + const { showArchived } = req.query; if (showArchived !== undefined) { // If showArchived is specified, use FeatureService.list with the parameter - let features = await FeatureService.list({ + const features = await FeatureService.list({ db: req.db, orgId: req.orgId, env: req.env, - archived: showArchived === "true" ? true : false, + archived: showArchived === "true", }); res.status(200).json({ features }); } else { // If no showArchived parameter, use the original getFromReq method - let features = await FeatureService.getFromReq(req); + const features = await FeatureService.getFromReq(req); res.status(200).json({ features }); } } catch (error: any) { @@ -40,14 +40,14 @@ internalFeatureRouter.get("", async (req: any, res: any) => { }); export const validateFeature = (data: any) => { - let featureType = data.type; + const featureType = data.type; validateFeatureId(data.id); let config = data.config; - if (featureType == FeatureType.Metered) { + if (featureType === FeatureType.Metered) { config = validateMeteredConfig(config); - } else if (featureType == FeatureType.CreditSystem) { + } else if (featureType === FeatureType.CreditSystem) { config = validateCreditSystem(config); } diff --git a/server/src/internal/products/ProductService.ts b/server/src/internal/products/ProductService.ts index c761185c8..f2ea4e79f 100644 --- a/server/src/internal/products/ProductService.ts +++ b/server/src/internal/products/ProductService.ts @@ -355,8 +355,6 @@ export class ProductService { internalId: string; update: any; }) { - console.log("internalId", internalId); - console.log("update", update); await db .update(products) .set(update) diff --git a/server/src/internal/products/handlers/handleUpdateProduct/handleUpdateProduct.ts b/server/src/internal/products/handlers/handleUpdateProduct/handleUpdateProduct.ts index e290294d4..52e19cd8e 100644 --- a/server/src/internal/products/handlers/handleUpdateProduct/handleUpdateProduct.ts +++ b/server/src/internal/products/handlers/handleUpdateProduct/handleUpdateProduct.ts @@ -77,6 +77,7 @@ export const handleUpdateProductV2 = createRoute({ const newProductV2: ProductV2 = { ...curProductV2, ...body, + group: body.group || curProductV2.group || "", items: body.items || [], free_trial: newFreeTrial || curProductV2.free_trial || undefined, }; @@ -149,7 +150,7 @@ export const handleUpdateProductV2 = createRoute({ // New full product const newFullProduct = await ProductService.getFull({ db, - idOrInternalId: fullProduct.id, + idOrInternalId: body.id || fullProduct.id, orgId: org.id, env, }); @@ -191,17 +192,17 @@ export const handleUpdateProductV2 = createRoute({ jobName: JobName.RewardMigration, payload: { oldPrices: fullProduct.prices, - productId: fullProduct.id, + productId: body.id || fullProduct.id, orgId: org.id, env, }, }); - return c.json( - getProductResponse({ - product: newFullProduct, - features, - }), - ); + const productResponse = await getProductResponse({ + product: newFullProduct, + features, + }); + + return c.json(productResponse); }, }); diff --git a/server/src/internal/products/handlers/handleUpdateProduct/updateProductDetails.ts b/server/src/internal/products/handlers/handleUpdateProduct/updateProductDetails.ts index ec18b84ec..f9beea3be 100644 --- a/server/src/internal/products/handlers/handleUpdateProduct/updateProductDetails.ts +++ b/server/src/internal/products/handlers/handleUpdateProduct/updateProductDetails.ts @@ -205,9 +205,7 @@ export const handleUpdateProductDetails = async ({ } } - if (productDetailsSame(curProduct, newProduct)) { - return; - } + if (productDetailsSame(curProduct, newProduct)) return; if (notNullish(newProduct.id) && newProduct.id !== curProduct.id) { if (customersOnAllVersions.length > 0) { @@ -225,6 +223,7 @@ export const handleUpdateProductDetails = async ({ } // 2. Update product + await ProductService.updateByInternalId({ db, internalId: curProduct.internal_id, diff --git a/server/src/internal/products/internalProductRouter.ts b/server/src/internal/products/internalProductRouter.ts index 0d5c15ead..b0a993c11 100644 --- a/server/src/internal/products/internalProductRouter.ts +++ b/server/src/internal/products/internalProductRouter.ts @@ -126,6 +126,14 @@ productRouter.get("/:productId/data2", async (req: any, res) => { const { version } = req.query; const { db, orgId, env } = req; + console.log("[/data2] Request params:", { + productId, + version, + orgId, + env, + featuresLength: req.features?.length, + }); + const [product, latestProduct] = await Promise.all([ ProductService.getFull({ db, @@ -146,9 +154,16 @@ productRouter.get("/:productId/data2", async (req: any, res) => { throw new ProductNotFoundError({ productId, version }); } + console.log( + "[/data2] Product found:", + product.id, + "Features available:", + req.features?.length || 0, + ); + const productV2 = mapToProductV2({ product: product, - features: req.features, + features: req.features || [], }); res.status(200).json({ @@ -158,8 +173,13 @@ productRouter.get("/:productId/data2", async (req: any, res) => { }, numVersions: latestProduct.version, }); - } catch (error) { + } catch (error: any) { console.error("Failed to get product", error); + console.error("Error details:", { + message: error?.message, + stack: error?.stack, + name: error?.name, + }); res.status(500).send(error); } }); diff --git a/shared/api/products/productOpModels.ts b/shared/api/products/productOpModels.ts index b7ca88a17..23918b4fb 100644 --- a/shared/api/products/productOpModels.ts +++ b/shared/api/products/productOpModels.ts @@ -35,7 +35,7 @@ export const UpdateProductV2ParamsSchema = z.object({ is_add_on: z.boolean().optional(), is_default: z.boolean().optional(), version: z.number().optional(), - group: z.string().optional(), + group: z.string().nullish(), archived: z.boolean().optional(), items: z.array(CreateProductItemParamsSchema).optional(), diff --git a/shared/models/productModels/productModels.ts b/shared/models/productModels/productModels.ts index ba8998a30..71e78cd0c 100644 --- a/shared/models/productModels/productModels.ts +++ b/shared/models/productModels/productModels.ts @@ -45,7 +45,7 @@ export const UpdateProductSchema = z.object({ name: z.string().min(1, "Product name cannot be empty").optional(), is_add_on: z.boolean().optional(), is_default: z.boolean().optional(), - group: z.string().optional(), + group: z.string().nullish(), archived: z.boolean().optional(), }); diff --git a/vite/src/views/onboarding3/hooks/actions/useFeatureCreationActions.tsx b/vite/src/views/onboarding3/hooks/actions/useFeatureCreationActions.tsx index da5b14ba2..40ef0aa82 100644 --- a/vite/src/views/onboarding3/hooks/actions/useFeatureCreationActions.tsx +++ b/vite/src/views/onboarding3/hooks/actions/useFeatureCreationActions.tsx @@ -32,9 +32,11 @@ export const useFeatureCreationActions = ({ setProduct, setBaseProduct, }: FeatureCreationActionsProps) => { - const { refetch: refetchFeatures } = useFeaturesQuery(); + const { features, refetch: refetchFeatures } = useFeaturesQuery(); + // Create feature and add to product const handleProceed = useCallback(async (): Promise => { + // 1. If feature already exists, update it, if not create it const createdFeature = await createFeature( feature as CreateFeature, axiosInstance, @@ -45,22 +47,9 @@ export const useFeatureCreationActions = ({ await refetchFeatures(); // Refresh features list - // Debug: Log the feature data used to create product item - console.log("FeatureCreationActions - created feature data:", { - id: createdFeature.id, - type: createdFeature.type, - usage_type: createdFeature.config?.usage_type, - fullConfig: createdFeature.config, - }); - // Create ProductItem and add to product immediately for live editing const newItem = createProductItem(createdFeature); - console.log("FeatureCreationActions - newItem created:", { - feature_id: newItem.feature_id, - feature_type: newItem.feature_type, - }); - setFeature(createdFeature); // Add feature item to product (preserving any existing base price item) diff --git a/vite/src/views/onboarding3/hooks/actions/usePlanDetailsActions.tsx b/vite/src/views/onboarding3/hooks/actions/usePlanDetailsActions.tsx index 289fb1b8c..223868086 100644 --- a/vite/src/views/onboarding3/hooks/actions/usePlanDetailsActions.tsx +++ b/vite/src/views/onboarding3/hooks/actions/usePlanDetailsActions.tsx @@ -23,42 +23,34 @@ export const usePlanDetailsActions = ({ productCreatedRef, setBaseProduct, }: PlanDetailsActionsProps) => { - const { products } = useProductsQuery(); + const { products, refetch: refetchProducts } = useProductsQuery(); // Create product and update base state const handleProceed = useCallback(async (): Promise => { - // console.log("Product:", product); // Check if base product exists in products query + + let newProduct: ProductV2; if (products.find((p) => p.id === baseProduct.id)) { - // Update product - await updateProduct({ + newProduct = await updateProduct({ axiosInstance, productId: baseProduct.id, product: product as ProductV2, - onSuccess: async () => { - // await handleRefetch(); - }, + onSuccess: async () => {}, }); - - console.log( - `Updating product, productId: ${baseProduct.id}, product:`, - product, - ); - return true; } else { - const createdProduct = await createProduct( + newProduct = await createProduct( product, axiosInstance, productCreatedRef, ); - - console.log(`Creating product, product:`, createdProduct); - - if (!createdProduct) return false; - - setBaseProduct(createdProduct); - return true; } + + if (!newProduct) return false; + + setBaseProduct(newProduct); + await refetchProducts(); + + return true; }, [ product, baseProduct, diff --git a/vite/src/views/onboarding3/hooks/useOnboardingData.tsx b/vite/src/views/onboarding3/hooks/useOnboardingData.tsx index 835200a3e..e3e4383f6 100644 --- a/vite/src/views/onboarding3/hooks/useOnboardingData.tsx +++ b/vite/src/views/onboarding3/hooks/useOnboardingData.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { useFeaturesQuery } from "@/hooks/queries/useFeaturesQuery"; import { useProductsQuery } from "@/hooks/queries/useProductsQuery"; import { useAxiosInstance } from "@/services/useAxiosInstance"; @@ -12,6 +12,7 @@ export const useOnboardingData = () => { isLoading: productsLoading, refetch: refetchProducts, } = useProductsQuery(); + const { features, isLoading: featuresLoading, @@ -21,8 +22,10 @@ export const useOnboardingData = () => { const { baseProduct, setBaseProduct, + feature, setFeature, + productCreatedRef, featureCreatedRef, isButtonLoading, @@ -65,6 +68,7 @@ export const useOnboardingData = () => { latestId: firstProduct.id, }; // loadProductData(firstProduct.id); + setBaseProduct(firstProduct); } @@ -143,15 +147,11 @@ export const useOnboardingData = () => { // ]); // Refetch product data - const handleRefetch = useCallback(async () => { - // if (product?.id) { - // await loadProductData(product.id); - // } - }, [product?.id]); + const handleRefetch = () => {}; - useEffect(() => { - console.log("[useOnboardingData] Feature: ", feature); - }, [feature]); + // useEffect(() => { + // console.log("[useOnboardingData] Feature: ", feature); + // }, [feature]); const isQueryLoading = useMemo(() => { return productsLoading || featuresLoading; diff --git a/vite/src/views/onboarding3/hooks/useOnboardingState.tsx b/vite/src/views/onboarding3/hooks/useOnboardingState.tsx index c0cc4adf6..1e7b96987 100644 --- a/vite/src/views/onboarding3/hooks/useOnboardingState.tsx +++ b/vite/src/views/onboarding3/hooks/useOnboardingState.tsx @@ -1,6 +1,7 @@ import { AppEnv, BillingInterval, + type Feature, type ProductItem, type ProductV2, } from "@autumn/shared"; @@ -29,6 +30,8 @@ export const useOnboardingState = () => { internal_id: "", }); + const [baseFeature, setBaseFeature] = useState(getDefaultFeature()); + // Feature creation state const [feature, setFeature] = useState(() => getDefaultFeature()); diff --git a/vite/src/views/onboarding3/utils/onboardingUtils.ts b/vite/src/views/onboarding3/utils/onboardingUtils.ts index f0f7b2065..099a3b95f 100644 --- a/vite/src/views/onboarding3/utils/onboardingUtils.ts +++ b/vite/src/views/onboarding3/utils/onboardingUtils.ts @@ -3,7 +3,6 @@ import { AppEnv, type CreateFeature, CreateFeatureSchema, - CreateProductSchema, type Feature, FeatureType, FeatureUsageType, @@ -159,39 +158,39 @@ export const handleBackNavigation = async ( setSelectedProductId: (id: string) => void, axiosInstance?: AxiosInstance, ) => { - // When going back to step 1 from step 2, load the created product - const currentStepNum = getStepNumber(step); - const willGoToStep1 = currentStepNum === 2; - - if (willGoToStep1 && productCreatedRef.current.created && productCreatedRef.current.latestId) { - // Load the created product data - if (axiosInstance) { - try { - const response = await axiosInstance.get( - `/products/${productCreatedRef.current.latestId}/data2`, - ); - setBaseProduct(response.data.product); - setSelectedProductId(productCreatedRef.current.latestId); - return; - } catch (error) { - console.error("Failed to load product on back navigation:", error); - } - } - } - - const isInConflictState = checkForStateConflicts( - step, - productCreatedRef, - baseProduct, - ); - - if (isInConflictState) { - resetCreationTracking(productCreatedRef, featureCreatedRef); - - const initialProduct = createInitialProductState(baseProduct.env); - setBaseProduct(initialProduct); - setSelectedProductId(""); - } + // // When going back to step 1 from step 2, load the created product + // const currentStepNum = getStepNumber(step); + // const willGoToStep1 = currentStepNum === 2; + // if ( + // willGoToStep1 && + // productCreatedRef.current.created && + // productCreatedRef.current.latestId + // ) { + // // Load the created product data + // if (axiosInstance) { + // try { + // const response = await axiosInstance.get( + // `/products/${productCreatedRef.current.latestId}/data2`, + // ); + // setBaseProduct(response.data.product); + // setSelectedProductId(productCreatedRef.current.latestId); + // return; + // } catch (error) { + // console.error("Failed to load product on back navigation:", error); + // } + // } + // } + // const isInConflictState = checkForStateConflicts( + // step, + // productCreatedRef, + // baseProduct, + // ); + // if (isInConflictState) { + // resetCreationTracking(productCreatedRef, featureCreatedRef); + // const initialProduct = createInitialProductState(baseProduct.env); + // setBaseProduct(initialProduct); + // setSelectedProductId(""); + // } }; // Handle plan selection logic @@ -262,50 +261,55 @@ export const createProduct = async ( }>, ) => { try { - const result = CreateProductSchema.safeParse({ - name: product?.name, - id: product?.id, - items: product?.items || [], - }); + // const result = CreateProductSchema.safeParse({ + // name: product?.name, + // id: product?.id, + // items: product?.items || [], + // }); - if (result.error) { - console.error("Product validation error:", result.error); - toast.error("Invalid product data"); - return null; - } + // if (result.error) { + // console.error("Product validation error:", result.error); + // toast.error("Invalid product data"); + // return null; + // } let createdProduct: Awaited< ReturnType >; - if (!productCreatedRef.current.created) { - // First time creating the product - createdProduct = await ProductService.createProduct( - axiosInstance, - result.data, - ); - productCreatedRef.current = { - created: true, - latestId: createdProduct.id, - }; - toast.success(`Product "${product?.name}" created successfully!`); - } else { - // Product already exists, update it (supports ID changes) - // Note: Backend creates a new product if ID changed, archives old one - await ProductService.updateProduct( - axiosInstance, - productCreatedRef.current.latestId as string, - result.data, - ); - // Fetch the full product after update (same as PlanEditorView does) - const response = await axiosInstance.get( - `/products/${result.data.id}/data2`, - ); - createdProduct = response.data.product; + createdProduct = await ProductService.createProduct(axiosInstance, product); + productCreatedRef.current = { + created: true, + latestId: createdProduct.id, + }; + toast.success(`Product "${product?.name}" created successfully!`); - productCreatedRef.current.latestId = result.data.id; - toast.success(`Product "${product?.name}" updated successfully!`); - } + // if (!productCreatedRef.current.created) { + // // First time creating the product + // createdProduct = await ProductService.createProduct( + // axiosInstance, + // product, + // ); + // productCreatedRef.current = { + // created: true, + // latestId: createdProduct.id, + // }; + // toast.success(`Product "${product?.name}" created successfully!`); + // } else { + // // Product already exists, update it (supports ID changes) + // // Note: Backend creates a new product if ID changed, archives old one + // await ProductService.updateProduct( + // axiosInstance, + // productCreatedRef.current.latestId as string, + // product, + // ); + // // Fetch the full product after update (same as PlanEditorView does) + // const response = await axiosInstance.get(`/products/${product.id}/data2`); + // createdProduct = response.data.product; + + // productCreatedRef.current.latestId = product.id; + // toast.success(`Product "${product?.name}" updated successfully!`); + // } return { ...createdProduct, @@ -393,15 +397,9 @@ export const createProductItem = (createdFeature: CreateFeature) => { // Map feature type to product item feature type let featureType: ProductItemFeatureType; - if ( - createdFeature.type === FeatureType.Boolean || - createdFeature.type === "boolean" - ) { + if (createdFeature.type === FeatureType.Boolean) { featureType = ProductItemFeatureType.Static; - } else if ( - createdFeature.type === FeatureType.CreditSystem || - createdFeature.type === "credit_system" - ) { + } else if (createdFeature.type === FeatureType.CreditSystem) { featureType = ProductItemFeatureType.SingleUse; } else if ( createdFeature.type === FeatureType.Metered || @@ -428,10 +426,7 @@ export const createProductItem = (createdFeature: CreateFeature) => { console.log("createProductItem - mapped to feature type:", featureType); // Boolean features have a simplified structure with no pricing/billing properties - if ( - createdFeature.type === FeatureType.Boolean || - createdFeature.type === "boolean" - ) { + if (createdFeature.type === FeatureType.Boolean) { return { feature_id: createdFeature.id, feature_type: featureType, diff --git a/vite/src/views/products/plan/components/edit-plan-details/BasePriceSection.tsx b/vite/src/views/products/plan/components/edit-plan-details/BasePriceSection.tsx index aba7b34d0..05495efd0 100644 --- a/vite/src/views/products/plan/components/edit-plan-details/BasePriceSection.tsx +++ b/vite/src/views/products/plan/components/edit-plan-details/BasePriceSection.tsx @@ -53,6 +53,7 @@ export const BasePriceSection = () => { intervalCount?: number; }) => { const newItems = [...product.items]; + // Find base price item by isBasePrice flag, not by price match const basePriceIndex = newItems.findIndex((item: ProductItem) => isPriceItem(item), diff --git a/vite/src/views/products/plan/components/new-feature/NewFeatureDetails.tsx b/vite/src/views/products/plan/components/new-feature/NewFeatureDetails.tsx index c358bd81d..fdf2f7beb 100644 --- a/vite/src/views/products/plan/components/new-feature/NewFeatureDetails.tsx +++ b/vite/src/views/products/plan/components/new-feature/NewFeatureDetails.tsx @@ -18,30 +18,31 @@ export function NewFeatureDetails({ targetKey: "id", }); - if (feature) - return ( - -
-
-
- Name - setSource(e.target.value)} - /> -
+ if (!feature) return null; -
- ID - setTarget(e.target.value)} - /> -
+ return ( + +
+
+
+ Name + setSource(e.target.value)} + /> +
+ +
+ ID + setTarget(e.target.value)} + />
- - ); +
+
+ ); } diff --git a/vite/src/views/products/product/utils/updateProduct.ts b/vite/src/views/products/product/utils/updateProduct.ts index eda2de1cf..7f8a85b2a 100644 --- a/vite/src/views/products/product/utils/updateProduct.ts +++ b/vite/src/views/products/product/utils/updateProduct.ts @@ -1,7 +1,7 @@ import { type FrontendProductItem, type ProductV2, - UpdateProductSchema, + UpdateProductV2ParamsSchema, } from "@autumn/shared"; import type { AxiosError, AxiosInstance } from "axios"; import { toast } from "sonner"; @@ -29,20 +29,24 @@ export const updateProduct = async ({ return false; } try { - const updateData = { - ...UpdateProductSchema.parse(product), + const updateData = UpdateProductV2ParamsSchema.parse({ + ...product, items: product.items, free_trial: product.free_trial, - }; + }); - await ProductService.updateProduct(axiosInstance, productId, updateData); + const updatedProduct = await ProductService.updateProduct( + axiosInstance, + productId, + updateData, + ); toast.success("Product updated successfully"); await onSuccess(); - return true; + return updatedProduct; } catch (error) { - console.error(error); + console.error((error as ZodError).message); toast.error( getBackendErr(error as AxiosError | ZodError, "Failed to update product"), );