From b8112f630bef7ad8212f7360c111b56fde2751d0 Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 11 Sep 2025 15:00:44 +0100 Subject: [PATCH] feat: toggle pro subscription --- apps/web/src/components/NewWorkspaceForm.tsx | 65 ++++++++++++++----- .../components/UpgradeToProConfirmation.tsx | 7 +- apps/web/src/views/settings/index.tsx | 9 ++- 3 files changed, 61 insertions(+), 20 deletions(-) diff --git a/apps/web/src/components/NewWorkspaceForm.tsx b/apps/web/src/components/NewWorkspaceForm.tsx index 3d0ccd5c..0bf95bfa 100644 --- a/apps/web/src/components/NewWorkspaceForm.tsx +++ b/apps/web/src/components/NewWorkspaceForm.tsx @@ -15,6 +15,7 @@ import { z } from "zod"; import Button from "~/components/Button"; import Input from "~/components/Input"; +import Toggle from "~/components/Toggle"; import { useDebounce } from "~/hooks/useDebounce"; import { useModal } from "~/providers/modal"; import { usePopup } from "~/providers/popup"; @@ -50,6 +51,7 @@ export function NewWorkspaceForm() { watch, trigger, clearErrors, + setValue, } = useForm({ resolver: zodResolver(schema), defaultValues: { @@ -68,6 +70,10 @@ export function NewWorkspaceForm() { const [debouncedSlug] = useDebounce(slug, 500); const isTyping = slug !== debouncedSlug; + // Pro toggle state management + const [isProToggleEnabled, setIsProToggleEnabled] = useState(false); + const [lastAvailableSlug, setLastAvailableSlug] = useState(""); + // Validate slug only after debounce useEffect(() => { if (isTyping) { @@ -104,12 +110,8 @@ export function NewWorkspaceForm() { role: "admin", }); - // If in cloud and user provided a valid slug, create checkout session for pro - if ( - env("NEXT_PUBLIC_KAN_ENV") === "cloud" && - slug && - isWorkspaceSlugAvailable?.isAvailable - ) { + // If in cloud and Pro toggle is enabled, create checkout session for pro + if (env("NEXT_PUBLIC_KAN_ENV") === "cloud" && isProToggleEnabled) { try { const response = await fetch( "/api/stripe/create_checkout_session", @@ -119,9 +121,9 @@ export function NewWorkspaceForm() { "Content-Type": "application/json", }, body: JSON.stringify({ - slug, + slug: slug || undefined, workspacePublicId: values.publicId, - cancelUrl: "/boards", + cancelUrl: "/settings?upgrade=pro", successUrl: "/boards", }), }, @@ -168,12 +170,21 @@ export function NewWorkspaceForm() { useEffect(() => { if (!checkWorkspaceSlugAvailability.isPending && isValidSlug) { - setShouldShowBenefits(isWorkspaceSlugAvailable?.isAvailable === true); + const isAvailable = isWorkspaceSlugAvailable?.isAvailable === true; + setShouldShowBenefits(isAvailable); + + // Automatically enable Pro toggle when slug is available (only in cloud env) + if (isAvailable && isCloudEnv) { + setIsProToggleEnabled(true); + setLastAvailableSlug(slug); + } } }, [ isValidSlug, isWorkspaceSlugAvailable?.isAvailable, checkWorkspaceSlugAvailability.isPending, + slug, + isCloudEnv, ]); // Reset benefits when slug becomes invalid @@ -183,7 +194,22 @@ export function NewWorkspaceForm() { } }, [isValidSlug]); - const showProBenefits = shouldShowBenefits; + // Handle Pro toggle changes + const handleProToggleChange = () => { + if (isProToggleEnabled) { + // Turning off: clear the slug and disable Pro + setValue("slug", ""); + setIsProToggleEnabled(false); + } else { + // Turning on: restore the last available slug if we have one + if (lastAvailableSlug) { + setValue("slug", lastAvailableSlug); + } + setIsProToggleEnabled(true); + } + }; + + const showProBenefits = isProToggleEnabled; const onSubmit = (values: FormValues) => { // Don't submit if slug is provided but not available @@ -278,11 +304,11 @@ export function NewWorkspaceForm() { }} /> - {showProBenefits && ( + {slug && slug.length >= 3 && shouldShowBenefits && (

- {t`Custom URLs require Pro plan ($29/month)`} + {t`Custom URLs require upgrading to a Pro plan`}

)} @@ -331,20 +357,27 @@ export function NewWorkspaceForm() { !showProBenefits && "mt-12", )} > -
+ {/* Pro Toggle - only show in cloud environment */} + {isCloudEnv && ( + + )} +
diff --git a/apps/web/src/views/settings/components/UpgradeToProConfirmation.tsx b/apps/web/src/views/settings/components/UpgradeToProConfirmation.tsx index c195ee72..ff30aa61 100644 --- a/apps/web/src/views/settings/components/UpgradeToProConfirmation.tsx +++ b/apps/web/src/views/settings/components/UpgradeToProConfirmation.tsx @@ -95,7 +95,12 @@ export function UpgradeToProConfirmation({
-