From c63589b2041a1dc6d16c2110d5e3420cab59a3d1 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Wed, 4 Feb 2026 21:31:46 -0800 Subject: [PATCH] auto open checkout modal --- src/components/GenerateLicenseKeysTable.tsx | 20 ++++++++++++++++++-- src/lib/cleanRedirect.ts | 18 +++++++++++++----- src/lib/internalRedirect.ts | 5 ++++- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/components/GenerateLicenseKeysTable.tsx b/src/components/GenerateLicenseKeysTable.tsx index 036b2fb5..48eeb045 100644 --- a/src/components/GenerateLicenseKeysTable.tsx +++ b/src/components/GenerateLicenseKeysTable.tsx @@ -10,8 +10,8 @@ import { Badge } from "./ui/badge"; import moment from "moment"; import { DataTable } from "./ui/data-table"; import { GeneratedLicenseKey } from "@server/routers/generatedLicense/types"; -import { useState } from "react"; -import { useRouter } from "next/navigation"; +import { useState, useEffect } from "react"; +import { useRouter, useSearchParams } from "next/navigation"; import { toast } from "@app/hooks/useToast"; import { createApiClient, formatAxiosError } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; @@ -29,12 +29,15 @@ function obfuscateLicenseKey(key: string): string { return `${firstPart}••••••••••••••••••••${lastPart}`; } +const GENERATE_QUERY = "generate"; + export default function GenerateLicenseKeysTable({ licenseKeys, orgId }: GnerateLicenseKeysTableProps) { const t = useTranslations(); const router = useRouter(); + const searchParams = useSearchParams(); const { env } = useEnvContext(); const api = createApiClient({ env }); @@ -42,6 +45,19 @@ export default function GenerateLicenseKeysTable({ const [isRefreshing, setIsRefreshing] = useState(false); const [showGenerateForm, setShowGenerateForm] = useState(false); + useEffect(() => { + if (searchParams.get(GENERATE_QUERY) !== null) { + setShowGenerateForm(true); + const next = new URLSearchParams(searchParams); + next.delete(GENERATE_QUERY); + const qs = next.toString(); + const url = qs + ? `${window.location.pathname}?${qs}` + : window.location.pathname; + window.history.replaceState(null, "", url); + } + }, [searchParams]); + const handleLicenseGenerated = () => { // Refresh the data after license is generated refreshData(); diff --git a/src/lib/cleanRedirect.ts b/src/lib/cleanRedirect.ts index 02a8dde1..512cbf2d 100644 --- a/src/lib/cleanRedirect.ts +++ b/src/lib/cleanRedirect.ts @@ -1,6 +1,8 @@ type CleanRedirectOptions = { fallback?: string; maxRedirectDepth?: number; + /** When true, preserve all query params on the path (for internal redirects). Default false. */ + allowAllQueryParams?: boolean; }; const ALLOWED_QUERY_PARAMS = new Set([ @@ -16,14 +18,18 @@ export function cleanRedirect( input: string, options: CleanRedirectOptions = {} ): string { - const { fallback = "/", maxRedirectDepth = 2 } = options; + const { + fallback = "/", + maxRedirectDepth = 2, + allowAllQueryParams = false + } = options; if (!input || typeof input !== "string") { return fallback; } try { - return sanitizeUrl(input, fallback, maxRedirectDepth); + return sanitizeUrl(input, fallback, maxRedirectDepth, allowAllQueryParams); } catch { return fallback; } @@ -32,7 +38,8 @@ export function cleanRedirect( function sanitizeUrl( input: string, fallback: string, - remainingRedirectDepth: number + remainingRedirectDepth: number, + allowAllQueryParams: boolean = false ): string { if ( input.startsWith("javascript:") || @@ -56,7 +63,7 @@ function sanitizeUrl( const cleanParams = new URLSearchParams(); for (const [key, value] of url.searchParams.entries()) { - if (!ALLOWED_QUERY_PARAMS.has(key)) { + if (!allowAllQueryParams && !ALLOWED_QUERY_PARAMS.has(key)) { continue; } @@ -68,7 +75,8 @@ function sanitizeUrl( const cleanedRedirect = sanitizeUrl( value, "", - remainingRedirectDepth - 1 + remainingRedirectDepth - 1, + allowAllQueryParams ); if (cleanedRedirect) { diff --git a/src/lib/internalRedirect.ts b/src/lib/internalRedirect.ts index 94830c58..115cea5c 100644 --- a/src/lib/internalRedirect.ts +++ b/src/lib/internalRedirect.ts @@ -28,7 +28,10 @@ export function consumeInternalRedirectPath(): string | null { return null; } - const cleaned = cleanRedirect(storedPath, { fallback: "" }); + const cleaned = cleanRedirect(storedPath, { + fallback: "", + allowAllQueryParams: true + }); if (!cleaned) return null; return cleaned.startsWith("/") ? cleaned : `/${cleaned}`;