diff --git a/CLAUDE.md b/CLAUDE.md index eb8f1d41d..e83a8b926 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,7 @@ - When using db schemas in Drizzle, import them from '@autumn/shared', and don't do schemas. # Linting and Codebase rules -- You can access the biome linter by running `npx biome check `. Always specify a folder path, as the codebase is quite large and you will get out of scope errors that you are not burdened to correct. If you would like to let biome automatically fix as much as it can, use `npx biome check --write ` +- You can access the biome linter by running `bunx biome check `. Always specify a folder path, as the codebase is quite large and you will get out of scope errors that you are not burdened to correct. If you would like to let biome automatically fix as much as it can, use `bunx biome check --write ` - Note, biome does not perform typechecking. In which case you need to, you may run `tsc --noEmit --skipLibCheck ` diff --git a/vite/src/components/v2/badges/PlanTypeBadge.tsx b/vite/src/components/v2/badges/PlanTypeBadge.tsx index 3076dca03..ae6eee4a8 100644 --- a/vite/src/components/v2/badges/PlanTypeBadge.tsx +++ b/vite/src/components/v2/badges/PlanTypeBadge.tsx @@ -21,9 +21,14 @@ const badgeVariants = cva( export interface PlanTypeBadgeProps extends VariantProps { className?: string; + iconOnly?: boolean; } -export const PlanTypeBadge = ({ variant, className }: PlanTypeBadgeProps) => { +export const PlanTypeBadge = ({ + variant, + className, + iconOnly, +}: PlanTypeBadgeProps) => { const getIcon = () => { switch (variant) { case "default": @@ -53,7 +58,7 @@ export const PlanTypeBadge = ({ variant, className }: PlanTypeBadgeProps) => { return (
{getIcon()} - {getLabel()} + {!iconOnly && {getLabel()}}
); }; diff --git a/vite/src/components/v2/badges/PlanTypeBadges.tsx b/vite/src/components/v2/badges/PlanTypeBadges.tsx index f771b95f5..f55128992 100644 --- a/vite/src/components/v2/badges/PlanTypeBadges.tsx +++ b/vite/src/components/v2/badges/PlanTypeBadges.tsx @@ -4,14 +4,24 @@ import { PlanTypeBadge } from "./PlanTypeBadge"; interface PlanTypeBadgesProps { product: ProductV2; className?: string; + iconOnly?: boolean; } -export const PlanTypeBadges = ({ product, className }: PlanTypeBadgesProps) => { +export const PlanTypeBadges = ({ + product, + className, + iconOnly = false, +}: PlanTypeBadgesProps) => { const badges = []; if (product.is_default) { badges.push( - , + , ); } @@ -21,13 +31,19 @@ export const PlanTypeBadges = ({ product, className }: PlanTypeBadgesProps) => { key="freeTrial" variant="freeTrial" className={className} + iconOnly={iconOnly} />, ); } if (product.is_add_on) { badges.push( - , + , ); } diff --git a/vite/src/components/v2/buttons/GroupedTabButton.tsx b/vite/src/components/v2/buttons/GroupedTabButton.tsx index b8cbf1bf2..0f0bbe912 100644 --- a/vite/src/components/v2/buttons/GroupedTabButton.tsx +++ b/vite/src/components/v2/buttons/GroupedTabButton.tsx @@ -39,9 +39,9 @@ export const GroupedTabButton = ({ "bg-light-purple text-primary shadow-[0px_3px_4px_0px_inset_rgba(0,0,0,0.04)]", !isActive && "bg-white shadow-[0px_-3px_4px_0px_inset_rgba(0,0,0,0.04)]", - isFirst && "rounded-l-md border-l", + isFirst && "rounded-l-lg border-l", !isFirst && "border-l-0", - isLast && "rounded-r-md", + isLast && "rounded-r-lg", )} > {isTwoTab && isFirst && option.icon && ( diff --git a/vite/src/views/onboarding3/OnboardingPreview.tsx b/vite/src/views/onboarding3/OnboardingPreview.tsx index 33f26b631..00fecc548 100644 --- a/vite/src/views/onboarding3/OnboardingPreview.tsx +++ b/vite/src/views/onboarding3/OnboardingPreview.tsx @@ -9,6 +9,7 @@ import { useProductsQuery } from "@/hooks/queries/useProductsQuery"; import { useFeatureStore } from "@/hooks/stores/useFeatureStore"; import { useProductStore } from "@/hooks/stores/useProductStore"; import { useIsEditingPlan, useSheetStore } from "@/hooks/stores/useSheetStore"; +import { cn } from "@/lib/utils"; import { keyToTitle } from "@/utils/formatUtils/formatTextUtils"; import { PlanCardToolbar } from "../products/plan/components/plan-card/PlanCardToolbar"; import { PlanFeatureList } from "../products/plan/components/plan-card/PlanFeatureList"; @@ -17,6 +18,7 @@ import { useOnboarding3QueryState } from "./hooks/useOnboarding3QueryState"; import { useOnboardingStore } from "./store/useOnboardingStore"; import { getStepNumber } from "./utils/onboardingUtils"; +const MAX_PLAN_NAME_LENGTH = 20; interface OnboardingPreviewProps { setConnectStripeOpen?: (open: boolean) => void; } @@ -79,36 +81,55 @@ export const OnboardingPreview = ({ } return ( - - -
-
- {showBasicInfo && product?.name ? ( - {product.name} - ) : ( - - Name your product - - )} + + + {/* Absolutely positioned toolbar - CANNOT MOVE */} + {showToolbar && ( +
+ +
+ )} - {playgroundMode === "edit" && product && ( - + {/* Left content with padding to avoid toolbar */} +
+
+ {showBasicInfo && product?.name ? ( +
+ + {product.name.length > MAX_PLAN_NAME_LENGTH + ? `${product.name.slice(0, MAX_PLAN_NAME_LENGTH)}...` + : product.name} + +
+ ) : ( +
+ Name your plan +
)}
-
- {showToolbar && ( - + MAX_PLAN_NAME_LENGTH - 10} /> - )} -
+
+ )}
{showPricing && ( @@ -134,7 +155,7 @@ export const OnboardingPreview = ({ {showDummyFeature && feature && ( <> - + )} diff --git a/vite/src/views/onboarding3/components/OnboardingStepRenderer.tsx b/vite/src/views/onboarding3/components/OnboardingStepRenderer.tsx index 4a47d6a87..e84585142 100644 --- a/vite/src/views/onboarding3/components/OnboardingStepRenderer.tsx +++ b/vite/src/views/onboarding3/components/OnboardingStepRenderer.tsx @@ -34,6 +34,7 @@ export const OnboardingStepRenderer = () => { const sheetType = useSheetStore((s) => s.type); const itemId = useSheetStore((s) => s.itemId); const [trackResponse, setTrackResponse] = useState(null); + const [checkResponse, setCheckResponse] = useState(null); const [lastUsedFeatureId, setLastUsedFeatureId] = useState< string | undefined >(undefined); @@ -183,10 +184,12 @@ export const OnboardingStepRenderer = () => { <> diff --git a/vite/src/views/onboarding3/components/PlanDetailsStep.tsx b/vite/src/views/onboarding3/components/PlanDetailsStep.tsx index 830b03334..7124a5b93 100644 --- a/vite/src/views/onboarding3/components/PlanDetailsStep.tsx +++ b/vite/src/views/onboarding3/components/PlanDetailsStep.tsx @@ -23,7 +23,7 @@ export const PlanDetailsStep = () => { return ( <> - +
@@ -36,7 +36,7 @@ export const PlanDetailsStep = () => { /> - The display name of the product that will show up on your + The display name of the plan that will show up on your checkout page
@@ -49,8 +49,7 @@ export const PlanDetailsStep = () => { className="mb-1" /> - A fixed price to charge for the product. Uncheck this section if - the product is free or a variable price. + Used to refer to this plan when using Autumn's APIs or SDKs
{/* {step === OnboardingStep.Playground && product && ( diff --git a/vite/src/views/onboarding3/components/integration-step/ConnectStripeSection.tsx b/vite/src/views/onboarding3/components/integration-step/ConnectStripeSection.tsx index ddcfa6f14..11cf6215c 100644 --- a/vite/src/views/onboarding3/components/integration-step/ConnectStripeSection.tsx +++ b/vite/src/views/onboarding3/components/integration-step/ConnectStripeSection.tsx @@ -41,7 +41,7 @@ export const ConnectStripeSection = () => { title="Connect Stripe" description={ - Stripe is required to checkout and add your products to customers. + Stripe is required to checkout and add your plans to customers. Grab your API key{" "} void; + onCheckSuccess?: (response: any) => void; onFeatureUsed?: (featureId: string) => void; }) => { - const { customer, track, refetch } = useCustomer(); + const { customer, track, refetch, check } = useCustomer(); const { features } = useFeaturesQuery(); return ( @@ -101,6 +103,16 @@ export const AvailableFeatures = ({ handleSend={async (value) => { const featureId = customer?.features[x].id; + // Check the feature access + const { data: checkResponse, error: checkError } = + await check({ + featureId: featureId, + requiredBalance: value, + }); + + if (!checkError && checkResponse && onCheckSuccess) { + onCheckSuccess(checkResponse); + } // Notify parent which feature was used if (onFeatureUsed && featureId !== undefined) { onFeatureUsed(featureId); @@ -123,8 +135,8 @@ export const AvailableFeatures = ({ )) ) : ( - Your current product doesn't have any features. Try purchasing a - product in the preview first. + Your current plan doesn't have any features. Try purchasing a + plan in the preview first. )}
diff --git a/vite/src/views/onboarding3/components/playground-step/PlaygroundToolbar.tsx b/vite/src/views/onboarding3/components/playground-step/PlaygroundToolbar.tsx index d4fe83c30..4d70fd421 100644 --- a/vite/src/views/onboarding3/components/playground-step/PlaygroundToolbar.tsx +++ b/vite/src/views/onboarding3/components/playground-step/PlaygroundToolbar.tsx @@ -50,30 +50,43 @@ export const PlaygroundToolbar = () => { }, ]} /> -
- - -
+ {playgroundMode === "edit" && ( +
+ {products.filter((p) => !p.archived).length > 1 && ( + + )} + +
+ )}
); }; diff --git a/vite/src/views/onboarding3/components/playground-step/QuickStartCodeGroup.tsx b/vite/src/views/onboarding3/components/playground-step/QuickStartCodeGroup.tsx index 7fb203932..5867dfec3 100644 --- a/vite/src/views/onboarding3/components/playground-step/QuickStartCodeGroup.tsx +++ b/vite/src/views/onboarding3/components/playground-step/QuickStartCodeGroup.tsx @@ -1,5 +1,6 @@ import type { ProductItem } from "@autumn/shared"; -import type { TrackResult } from "autumn-js"; +import type { CheckResult, TrackResult } from "autumn-js"; +import { useCustomer } from "autumn-js/react"; import { useEffect, useState } from "react"; import { CodeGroup, @@ -10,7 +11,8 @@ import { CodeGroupTab, } from "@/components/v2/CodeGroup"; import { SheetSection } from "@/components/v2/sheets/InlineSheet"; -import { useProductContext } from "@/views/products/product/ProductContext"; +import { useFeaturesQuery } from "@/hooks/queries/useFeaturesQuery"; +import { useProductStore } from "@/hooks/stores/useProductStore"; import { getCodeSnippets } from "../../utils/completionStepCode"; type CodeLanguage = "react" | "nodejs" | "response"; @@ -19,28 +21,34 @@ const CodeSnippetSection = ({ title, snippets, trackResponse, + checkResponse, }: { title: string; snippets: { react: string; nodejs: string; response: string }; trackResponse?: TrackResult; + checkResponse?: CheckResult; }) => { const [activeLanguage, setActiveLanguage] = useState("react"); - // Auto-switch to response tab when trackResponse is available (for track section only) + // Auto-switch to response tab when responses are available useEffect(() => { if (trackResponse && title === "Track usage") { setActiveLanguage("response"); } - }, [trackResponse, title]); + if (checkResponse && title === "Check feature access") { + setActiveLanguage("response"); + } + }, [trackResponse, checkResponse, title]); const getCodeForTab = () => { - // Use dynamic trackResponse for track section response tab - if ( - activeLanguage === "response" && - title === "Track usage" && - trackResponse - ) { - return JSON.stringify(trackResponse, null, 2); + // Use dynamic responses for response tabs + if (activeLanguage === "response") { + if (title === "Track usage" && trackResponse) { + return JSON.stringify(trackResponse, null, 2); + } + if (title === "Check feature access" && checkResponse) { + return JSON.stringify(checkResponse, null, 2); + } } return snippets[activeLanguage]; }; @@ -70,7 +78,35 @@ const CodeSnippetSection = ({ {title === "Track usage" && trackResponse ? JSON.stringify(trackResponse, null, 2) - : snippets.response} + : title === "Check feature access" && checkResponse + ? JSON.stringify(checkResponse, null, 2) + : snippets.response} + + + + + ); +}; + +const CustomerSection = () => { + const { customer } = useCustomer(); + const [activeTab, setActiveTab] = useState("customer"); + + return ( +
+

Customer

+ + + Customer Response + + navigator.clipboard.writeText(JSON.stringify(customer, null, 2)) + } + /> + + + + {JSON.stringify(customer, null, 2)} @@ -80,12 +116,15 @@ const CodeSnippetSection = ({ export const QuickStartCodeGroup = ({ trackResponse, + checkResponse, featureId: usedFeatureId, }: { trackResponse?: TrackResult; + checkResponse?: CheckResult; featureId?: string; }) => { - const { product } = useProductContext(); + const { product } = useProductStore(); + const { features } = useFeaturesQuery(); // Use the feature that was actually used (if available), otherwise fallback to first feature const firstFeatureItem = product?.items?.find( @@ -94,8 +133,11 @@ export const QuickStartCodeGroup = ({ const featureId = usedFeatureId || firstFeatureItem?.feature_id || undefined; const productId = product?.id || undefined; + // Get the actual feature name from features list + const featureName = features.find((f) => f.id === featureId)?.name; + // Generate snippets with actual IDs from onboarding - const snippets = getCodeSnippets(featureId, productId); + const snippets = getCodeSnippets(featureId, productId, featureName); return ( @@ -103,6 +145,7 @@ export const QuickStartCodeGroup = ({ +
); diff --git a/vite/src/views/onboarding3/hooks/actions/usePlanDetailsActions.tsx b/vite/src/views/onboarding3/hooks/actions/usePlanDetailsActions.tsx index 8b985aeb1..eba1f1607 100644 --- a/vite/src/views/onboarding3/hooks/actions/usePlanDetailsActions.tsx +++ b/vite/src/views/onboarding3/hooks/actions/usePlanDetailsActions.tsx @@ -38,7 +38,7 @@ export const usePlanDetailsActions = () => { product: product as ProductV2, onSuccess: async () => {}, }); - toast.success("Product updated successfully"); + toast.success("Plan updated successfully"); } else { // Create new product newProduct = await createProduct(product, axiosInstance); diff --git a/vite/src/views/onboarding3/utils/completionStepCode.ts b/vite/src/views/onboarding3/utils/completionStepCode.ts index 208afcccd..6ad79a1cc 100644 --- a/vite/src/views/onboarding3/utils/completionStepCode.ts +++ b/vite/src/views/onboarding3/utils/completionStepCode.ts @@ -1,6 +1,11 @@ -export const getCodeSnippets = (featureId?: string, productId?: string) => { +export const getCodeSnippets = ( + featureId?: string, + productId?: string, + featureName?: string, +) => { const actualFeatureId = featureId || "your_feature_id"; const actualProductId = productId || "your_product_id"; + const actualFeatureName = featureName || "Your Feature"; return { allowed: { @@ -27,7 +32,7 @@ const allowed = await autumn.check({ "allowed": true, "feature": { "id": "${actualFeatureId}", - "name": "Your Feature", + "name": "${actualFeatureName}", "type": "limit" } }`, @@ -73,7 +78,7 @@ console.log(session.checkout_url);`, "customer_id": "cust_123", "product": { "id": "${actualProductId}", - "name": "Your Product", + "name": "Your Plan", "items": [ { "type": "price", diff --git a/vite/src/views/onboarding3/utils/onboardingUtils.ts b/vite/src/views/onboarding3/utils/onboardingUtils.ts index 187655dae..53052febf 100644 --- a/vite/src/views/onboarding3/utils/onboardingUtils.ts +++ b/vite/src/views/onboarding3/utils/onboardingUtils.ts @@ -61,19 +61,19 @@ export const getNextStep = ( // Step configuration for headers and descriptions export const stepConfig = { [OnboardingStep.PlanDetails]: { - title: "Create a product", + title: "Create a plan", description: - "Products are the pricing tiers your application offers. You can create your free tiers and your paid tiers too.", + "Plans are the pricing tiers your application offers. You can create your free tiers and your paid tiers too.", }, [OnboardingStep.FeatureCreation]: { title: "Create a feature", description: - "Create and add the first feature that customers on this plan get access to. One feature for each part of your app you want to gate based on pricing.", + "Features are the benefits customers get access to when using this plan. You can create a feature for things in your app you want to limit, track or bill for.", }, [OnboardingStep.FeatureConfiguration]: { title: "Define feature limits or billing", description: - "Features can be included as part of this product, or billed for based on their usage.", + "Features can be included as part of this plan, or billed for based on their usage.", }, [OnboardingStep.Playground]: { title: "Finish your setup", @@ -273,7 +273,7 @@ export const createProduct = async ( // created: true, // latestId: createdProduct.id, // }; - toast.success(`Product "${product?.name}" created successfully!`); + toast.success(`Plan "${product?.name}" created successfully!`); // if (!productCreatedRef.current.created) { // // First time creating the product diff --git a/vite/src/views/products/ProductConfig.tsx b/vite/src/views/products/ProductConfig.tsx index a8f469b81..ba1eb23c1 100644 --- a/vite/src/views/products/ProductConfig.tsx +++ b/vite/src/views/products/ProductConfig.tsx @@ -28,7 +28,7 @@ export const ProductConfig = ({
Name { setSource(e.target.value); @@ -40,7 +40,7 @@ export const ProductConfig = ({
{ if (product.archived) { - return `Are you sure you want to unarchive ${product.name}? This will make it visible in your list of products.`; + return `Are you sure you want to unarchive ${product.name}? This will make it visible in your list of plans.`; } // \n\nNote: If there are multiple versions, this will unarchive all versions at once. const isMultipleVersions = productInfo?.numVersion > 1; - const versionText = deleteAllVersions ? "product" : "version"; - const productText = isMultipleVersions ? versionText : "product"; + const versionText = deleteAllVersions ? "plan" : "version"; + const productText = isMultipleVersions ? versionText : "plan"; const messageTemplates = { withCustomers: { @@ -151,9 +151,9 @@ export const DeletePlanDialog = ({ otherCount: number, productText: string, ) => - `${customerName} and ${otherCount} other customer${otherCount > 1 ? "s" : ""} are on this ${productText}. Are you sure you want to archive this product?`, + `${customerName} and ${otherCount} other customer${otherCount > 1 ? "s" : ""} are on this ${productText}. Are you sure you want to archive this plan?`, fallback: (productText: string) => - `There are customers on this ${productText}. Deleting this ${productText} will remove it from their accounts. Are you sure you want to continue? You can also archive the product instead.`, + `There are customers on this ${productText}. Deleting this ${productText} will remove it from their accounts. Are you sure you want to continue? You can also archive the plan instead.`, }, withoutCustomers: (productText: string) => `Are you sure you want to delete this ${productText}? This action cannot be undone.`, @@ -224,7 +224,7 @@ export const DeletePlanDialog = ({ Delete latest version - Archive product + Archive plan )} diff --git a/vite/src/views/products/plan/components/EditPlanHeader.tsx b/vite/src/views/products/plan/components/EditPlanHeader.tsx index cb28a0d51..7743237bc 100644 --- a/vite/src/views/products/plan/components/EditPlanHeader.tsx +++ b/vite/src/views/products/plan/components/EditPlanHeader.tsx @@ -110,7 +110,7 @@ export const EditPlanHeader = () => { className="p-0" items={[ { - name: "Products", + name: "Plans", href: "/products?tab=products", }, { diff --git a/vite/src/views/products/plan/components/EditPlanSheet.tsx b/vite/src/views/products/plan/components/EditPlanSheet.tsx index bc80f02a8..b388e3332 100644 --- a/vite/src/views/products/plan/components/EditPlanSheet.tsx +++ b/vite/src/views/products/plan/components/EditPlanSheet.tsx @@ -14,7 +14,7 @@ export function EditPlanSheet({ isOnboarding }: { isOnboarding?: boolean }) { <> {!isOnboarding && ( )} diff --git a/vite/src/views/products/plan/components/SaveChangesBar.tsx b/vite/src/views/products/plan/components/SaveChangesBar.tsx index 7012e4ab4..4aabb5081 100644 --- a/vite/src/views/products/plan/components/SaveChangesBar.tsx +++ b/vite/src/views/products/plan/components/SaveChangesBar.tsx @@ -46,7 +46,7 @@ export const SaveChangesBar = ({ const handleSaveClicked = async () => { if (!isOnboarding && isLoading) { - toast.error("Product counts are loading"); + toast.error("Plan counts are loading"); return; } diff --git a/vite/src/views/products/plan/components/SelectFeatureSheet.tsx b/vite/src/views/products/plan/components/SelectFeatureSheet.tsx index 5f82e3229..a30f72e68 100644 --- a/vite/src/views/products/plan/components/SelectFeatureSheet.tsx +++ b/vite/src/views/products/plan/components/SelectFeatureSheet.tsx @@ -62,7 +62,7 @@ export function SelectFeatureSheet({ {!isOnboarding && ( )} diff --git a/vite/src/views/products/plan/components/edit-plan-details/AdditionalOptions.tsx b/vite/src/views/products/plan/components/edit-plan-details/AdditionalOptions.tsx index acd74fcbb..2a20d55e9 100644 --- a/vite/src/views/products/plan/components/edit-plan-details/AdditionalOptions.tsx +++ b/vite/src/views/products/plan/components/edit-plan-details/AdditionalOptions.tsx @@ -15,8 +15,8 @@ export const AdditionalOptions = ({
@@ -25,8 +25,8 @@ export const AdditionalOptions = ({ /> diff --git a/vite/src/views/products/plan/components/edit-plan-details/MainDetailsSection.tsx b/vite/src/views/products/plan/components/edit-plan-details/MainDetailsSection.tsx index bc36f02ee..ae3d99d10 100644 --- a/vite/src/views/products/plan/components/edit-plan-details/MainDetailsSection.tsx +++ b/vite/src/views/products/plan/components/edit-plan-details/MainDetailsSection.tsx @@ -8,7 +8,7 @@ export const MainDetailsSection = () => { const setProduct = useProductStore((s) => s.setProduct); return ( - +
diff --git a/vite/src/views/products/plan/components/edit-plan-feature/AdvancedSettings.tsx b/vite/src/views/products/plan/components/edit-plan-feature/AdvancedSettings.tsx index d93ab7a01..be4d037a9 100644 --- a/vite/src/views/products/plan/components/edit-plan-feature/AdvancedSettings.tsx +++ b/vite/src/views/products/plan/components/edit-plan-feature/AdvancedSettings.tsx @@ -81,10 +81,10 @@ export function AdvancedSettings() { description="Additional configuration options for this feature" >
- {/* Reset existing usage when product is enabled */} + {/* Reset existing usage when plan is enabled */}
} onClick={onEdit} - aria-label="Edit product" + aria-label="Edit plan" variant="muted" disabled={editDisabled} iconOrientation="center" @@ -75,7 +75,7 @@ export const PlanCardToolbar = ({ } onClick={() => setDeleteOpen(true)} - aria-label="Delete product" + aria-label="Delete plan" variant="muted" iconOrientation="center" disabled={deleteDisabled} diff --git a/vite/src/views/products/plan/hooks/useProductChangedAlert.tsx b/vite/src/views/products/plan/hooks/useProductChangedAlert.tsx index fd4e5eda1..89da89a8f 100644 --- a/vite/src/views/products/plan/hooks/useProductChangedAlert.tsx +++ b/vite/src/views/products/plan/hooks/useProductChangedAlert.tsx @@ -2,7 +2,7 @@ import { useCallback } from "react"; import { useBlocker } from "@/views/products/product/hooks/useBlocker"; const DEFAULT_MESSAGE = - "Are you sure you want to leave without updating the product? Your changes will be lost."; + "Are you sure you want to leave without updating the plan? Your changes will be lost."; export const useProductChangedAlert = ({ hasChanges, diff --git a/vite/src/views/products/product/ProductSidebar.tsx b/vite/src/views/products/product/ProductSidebar.tsx index 1df61398c..4a359207c 100644 --- a/vite/src/views/products/product/ProductSidebar.tsx +++ b/vite/src/views/products/product/ProductSidebar.tsx @@ -91,7 +91,7 @@ export default function ProductSidebar() { } disabledReason={ isOneOffProduct(product.items, product.is_add_on) - ? "Can't add a free trial to an a one time product" + ? "Can't add a free trial to an a one time plan" : undefined } > @@ -100,7 +100,7 @@ export default function ProductSidebar() { ) : ( - Add a free trial to this product. + Add a free trial to this plan. )}
diff --git a/vite/src/views/products/product/ProductView.tsx b/vite/src/views/products/product/ProductView.tsx index 52379ac97..b9631fd60 100644 --- a/vite/src/views/products/product/ProductView.tsx +++ b/vite/src/views/products/product/ProductView.tsx @@ -35,7 +35,7 @@ function ProductView() { if (error) { return ( - {error ? error.message : `Product ${product_id} not found`} + {error ? error.message : `Plan ${product_id} not found`} ); } diff --git a/vite/src/views/products/product/components/ProductViewBreadcrumbs.tsx b/vite/src/views/products/product/components/ProductViewBreadcrumbs.tsx index 7e510b5fd..42396eb69 100644 --- a/vite/src/views/products/product/components/ProductViewBreadcrumbs.tsx +++ b/vite/src/views/products/product/components/ProductViewBreadcrumbs.tsx @@ -22,7 +22,7 @@ export default function ProductViewBreadcrumbs() { onClick={() => navigateTo("/products", navigate, env)} className="cursor-pointer" > - Products + Plans diff --git a/vite/src/views/products/product/hooks/NavigationBlockerModal.tsx b/vite/src/views/products/product/hooks/NavigationBlockerModal.tsx index e880fe123..ad7e395fe 100644 --- a/vite/src/views/products/product/hooks/NavigationBlockerModal.tsx +++ b/vite/src/views/products/product/hooks/NavigationBlockerModal.tsx @@ -26,7 +26,7 @@ export const NavigationBlockerModal: React.FC = ({ Unsaved Changes - Are you sure you want to leave without updating the product? Your + Are you sure you want to leave without updating the plan? Your changes will be lost. diff --git a/vite/src/views/products/product/product-sidebar/ToggleDefaultProduct.tsx b/vite/src/views/products/product/product-sidebar/ToggleDefaultProduct.tsx index 26e4922d7..389afcbaa 100644 --- a/vite/src/views/products/product/product-sidebar/ToggleDefaultProduct.tsx +++ b/vite/src/views/products/product/product-sidebar/ToggleDefaultProduct.tsx @@ -49,7 +49,7 @@ const ToggleProductDialog = ({ const getTitle = () => { if (toggleKey === "is_default") { return value - ? `Make ${product.name} a default product` + ? `Make ${product.name} a default plan` : `Remove default from ${product.name}`; } else { return value @@ -119,14 +119,14 @@ export const ToggleDefaultProduct = ({ await ProductService.updateProduct(axiosInstance, product.id, data); // mutate(); setOpen(false); - toast.success("Successfully updated product"); + toast.success("Successfully updated plan"); } catch (error) { setProduct({ ...product, [toggleKey]: !value, }); - toast.error(getBackendErr(error, "Failed to update product")); + toast.error(getBackendErr(error, "Failed to update plan")); } finally { setToggling(false); } @@ -148,21 +148,21 @@ export const ToggleDefaultProduct = ({ if (toggleKey === "is_default") { if (value) { setDialogDescription( - `You have ${activeCount} active ${activeCusStr} on this product. Are you sure you want to make this product default?`, + `You have ${activeCount} active ${activeCusStr} on this plan. Are you sure you want to make this plan default?`, ); } else { setDialogDescription( - `You have ${activeCount} active ${activeCusStr} on this product. Are you sure you want to remove this product as default?`, + `You have ${activeCount} active ${activeCusStr} on this plan. Are you sure you want to remove this plan as default?`, ); } } else { if (value) { setDialogDescription( - `You have ${activeCount} active ${activeCusStr} on this product. Are you sure you want to make this product an add-on?`, + `You have ${activeCount} active ${activeCusStr} on this plan. Are you sure you want to make this plan an add-on?`, ); } else { setDialogDescription( - `You have ${activeCount} active ${activeCusStr} on this product. Are you sure you want to remove this product as an add-on?`, + `You have ${activeCount} active ${activeCusStr} on this plan. Are you sure you want to remove this plan as an add-on?`, ); } } @@ -180,11 +180,11 @@ export const ToggleDefaultProduct = ({ value && product.free_trial && !isFreeProductV2(product); if (isDefaultTrial && notNullish(groupDefaults?.defaultTrial)) { - return `${groupDefaults.defaultTrial.name} is currently a default trial product. Making ${product.name} a default trial will remove ${groupDefaults.defaultTrial.name} as a default trial product.`; + return `${groupDefaults.defaultTrial.name} is currently a default trial plan. Making ${product.name} a default trial will remove ${groupDefaults.defaultTrial.name} as a default trial plan.`; } if (value && notNullish(groupDefaults?.free)) { - return `${groupDefaults.free.name} is currently a default product. Making ${product.name} a default product will remove ${groupDefaults.free.name} as a default product.`; + return `${groupDefaults.free.name} is currently a default plan. Making ${product.name} a default plan will remove ${groupDefaults.free.name} as a default plan.`; } }; diff --git a/vite/src/views/products/products/components/CreatePlanDialog.tsx b/vite/src/views/products/products/components/CreatePlanDialog.tsx index 08be0cb28..a641a0a44 100644 --- a/vite/src/views/products/products/components/CreatePlanDialog.tsx +++ b/vite/src/views/products/products/components/CreatePlanDialog.tsx @@ -47,8 +47,8 @@ function CreatePlanDialog({ if (!/^[a-zA-Z0-9 _-]+$/.test(productName)) { toast.error( !productName - ? "Product name is required" - : "Product name can only contain alphanumeric characters, dashes (-), and underscores (_)", + ? "Plan name is required" + : "Plan name can only contain alphanumeric characters, dashes (-), and underscores (_)", ); return; } @@ -67,7 +67,7 @@ function CreatePlanDialog({ } setOpen(false); } catch (error) { - toast.error(getBackendErr(error, "Failed to create product")); + toast.error(getBackendErr(error, "Failed to create plan")); } setLoading(false); }; @@ -88,11 +88,11 @@ function CreatePlanDialog({ className={buttonClassName} onClick={() => setOpen(true)} > - Add Product + Add Plan - Create Product + Create Plan - Create Product + Create Plan diff --git a/vite/src/views/products/products/components/CreateProductDialog.tsx b/vite/src/views/products/products/components/CreateProductDialog.tsx index 6846e056d..c24a8bfd5 100644 --- a/vite/src/views/products/products/components/CreateProductDialog.tsx +++ b/vite/src/views/products/products/components/CreateProductDialog.tsx @@ -46,8 +46,8 @@ function CreateProduct({ if (!/^[a-zA-Z0-9 _-]+$/.test(productName)) { toast.error( !productName - ? "Product name is required" - : "Product name can only contain alphanumeric characters, dashes (-), and underscores (_)", + ? "Plan name is required" + : "Plan name can only contain alphanumeric characters, dashes (-), and underscores (_)", ); return; } @@ -66,7 +66,7 @@ function CreateProduct({ } setOpen(false); } catch (error) { - toast.error(getBackendErr(error, "Failed to create product")); + toast.error(getBackendErr(error, "Failed to create plan")); } setLoading(false); }; @@ -83,11 +83,11 @@ function CreateProduct({ - Create Product + Create Plan - Create Product + Create Plan
diff --git a/vite/src/views/products/products/components/CreateProductMainDetails.tsx b/vite/src/views/products/products/components/CreateProductMainDetails.tsx index 48d3d39ca..66409510e 100644 --- a/vite/src/views/products/products/components/CreateProductMainDetails.tsx +++ b/vite/src/views/products/products/components/CreateProductMainDetails.tsx @@ -16,13 +16,13 @@ export const CreateProductMainDetails = () => { }); return ( - +
Name setSource(e.target.value)} />