From 540aee3fe2d2dc879eb6653f7ab7341b66a3a0f4 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Wed, 10 Jun 2026 17:52:33 -0700 Subject: [PATCH 1/7] update docs links --- messages/en-US.json | 1 + src/app/[orgId]/settings/general/page.tsx | 2 +- .../resources/public/[niceId]/rdp/page.tsx | 2 +- .../resources/public/[niceId]/ssh/page.tsx | 6 +++--- .../settings/resources/public/create/page.tsx | 10 ++++----- .../settings/sites/[niceId]/general/page.tsx | 2 +- src/app/ssh/SshClient.tsx | 2 +- src/components/BrowserGatewayTargetForm.tsx | 2 +- src/components/PrivateResourceForm.tsx | 2 +- src/components/ResourcePoliciesBanner.tsx | 21 +++++++++++++++++-- 10 files changed, 34 insertions(+), 16 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index 48ad74542..f033e96f4 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -214,6 +214,7 @@ "resourceErrorDelte": "Error deleting resource", "resourcePoliciesBannerTitle": "Re-use Authentication and Access Rules", "resourcePoliciesBannerDescription": "Shared resource policies let you define authentication methods and access rules once, then attach them to multiple public resources. When you update a policy, every linked resource inherits the change automatically.", + "resourcePoliciesBannerButtonText": "Learn More", "resourcePoliciesTitle": "Manage Public Resource Policies", "resourcePoliciesAttachedResourcesColumnTitle": "Resources", "resourcePoliciesAttachedResources": "{count} resource(s)", diff --git a/src/app/[orgId]/settings/general/page.tsx b/src/app/[orgId]/settings/general/page.tsx index 0ebfae651..e20523250 100644 --- a/src/app/[orgId]/settings/general/page.tsx +++ b/src/app/[orgId]/settings/general/page.tsx @@ -282,7 +282,7 @@ function GeneralSectionForm({ org }: SectionFormProps) { {t("newtAutoUpdateDescription")}{" "} diff --git a/src/app/[orgId]/settings/resources/public/[niceId]/ssh/page.tsx b/src/app/[orgId]/settings/resources/public/[niceId]/ssh/page.tsx index 3629ed7cd..8696e1967 100644 --- a/src/app/[orgId]/settings/resources/public/[niceId]/ssh/page.tsx +++ b/src/app/[orgId]/settings/resources/public/[niceId]/ssh/page.tsx @@ -467,7 +467,7 @@ function SshServerForm({

{t("sshDaemonDisclaimer")}{" "} @@ -602,7 +602,7 @@ function SshServerForm({ siteField="selectedSite" destinationField="destination" destinationPortField="destinationPort" - learnMoreHref="https://docs.pangolin.net/manage/resources/public/ssh" + learnMoreHref="https://docs.pangolin.net/manage/resources/public/ssh#site-and-host-configuration" defaultPort={22} /> diff --git a/src/app/[orgId]/settings/resources/public/create/page.tsx b/src/app/[orgId]/settings/resources/public/create/page.tsx index 08c2093d7..b3810c562 100644 --- a/src/app/[orgId]/settings/resources/public/create/page.tsx +++ b/src/app/[orgId]/settings/resources/public/create/page.tsx @@ -1097,7 +1097,7 @@ export default function Page() { "sshDaemonDisclaimer" )}{" "} @@ -1256,7 +1256,7 @@ export default function Page() { siteField="selectedSite" destinationField="destination" destinationPortField="destinationPort" - learnMoreHref="https://docs.pangolin.net/manage/resources/public/ssh" + learnMoreHref="https://docs.pangolin.net/manage/resources/public/ssh#site-and-host-configuration" defaultPort={22} /> @@ -1306,7 +1306,7 @@ export default function Page() { sitesField="selectedSites" destinationField="destination" destinationPortField="destinationPort" - learnMoreHref="https://docs.pangolin.net/manage/resources/public/rdp" + learnMoreHref="https://docs.pangolin.net/manage/resources/public/rdp#site-and-host-configuration" defaultPort={3389} /> @@ -1353,7 +1353,7 @@ export default function Page() { sitesField="selectedSites" destinationField="destination" destinationPortField="destinationPort" - learnMoreHref="https://docs.pangolin.net/manage/resources/public/vnc" + learnMoreHref="https://docs.pangolin.net/manage/resources/public/vnc#site-and-host-configuration" defaultPort={5900} /> diff --git a/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx b/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx index 4f1b8035f..8d5a654c9 100644 --- a/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx +++ b/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx @@ -317,7 +317,7 @@ export default function GeneralPage() { "siteAutoUpdateDescription" )}{" "} {t("sshPrivateKeyDisclaimer")}{" "} ( {t("sshDaemonDisclaimer")}{" "} { @@ -14,7 +16,22 @@ export const ResourcePoliciesBanner = () => { title={t("resourcePoliciesBannerTitle")} titleIcon={} description={t("resourcePoliciesBannerDescription")} - /> + > + + + + ); }; From f64d04e82773e76d63979fe1202a33b76fa56492 Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 10 Jun 2026 18:23:01 -0700 Subject: [PATCH 2/7] Add loading back to create resource --- src/app/[orgId]/settings/resources/public/create/page.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/app/[orgId]/settings/resources/public/create/page.tsx b/src/app/[orgId]/settings/resources/public/create/page.tsx index 08c2093d7..7e79ddbd5 100644 --- a/src/app/[orgId]/settings/resources/public/create/page.tsx +++ b/src/app/[orgId]/settings/resources/public/create/page.tsx @@ -80,7 +80,6 @@ import { toASCII } from "punycode"; import { useMemo, useState, - useTransition, useEffect } from "react"; import { useForm, type Resolver } from "react-hook-form"; @@ -229,7 +228,7 @@ export default function Page() { >([]); const [loadingExitNodes, setLoadingExitNodes] = useState(build === "saas"); - const [createLoading, startTransition] = useTransition(); + const [createLoading, setCreateLoading] = useState(false); const [showSnippets, setShowSnippets] = useState(false); const [niceId, setNiceId] = useState(""); @@ -461,6 +460,7 @@ export default function Page() { }; async function onSubmit() { + setCreateLoading(true); const baseData = baseForm.getValues(); try { @@ -707,6 +707,8 @@ export default function Page() { t("resourceErrorCreateMessageDescription") ) }); + } finally { + setCreateLoading(false); } } @@ -1427,7 +1429,7 @@ export default function Page() { } }} loading={createLoading} - disabled={!areAllTargetsValid() || browserGatewayDisabled} + disabled={!areAllTargetsValid() || browserGatewayDisabled || createLoading} > {t("resourceCreate")} From 1a942937e67621eaf98630ac9c70d0d2dff3fda9 Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 10 Jun 2026 20:24:41 -0700 Subject: [PATCH 3/7] Remove precheck on websocket for now --- src/app/vnc/VncClient.tsx | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/app/vnc/VncClient.tsx b/src/app/vnc/VncClient.tsx index 8a580c14a..ac50bb587 100644 --- a/src/app/vnc/VncClient.tsx +++ b/src/app/vnc/VncClient.tsx @@ -124,21 +124,21 @@ export default function VncClient({ authToken: target.authToken }); - try { - const checkParams = new URLSearchParams(params); - checkParams.set("checkOnly", "1"); - const response = await fetch(`${base}?${checkParams.toString()}`); - if (!response.ok) { - const detail = (await response.text()).trim(); - setConnectError(detail || t("sshErrorConnectionClosed")); - setConnecting(false); - return; - } - } catch { - setConnectError(t("sshErrorWebSocket")); - setConnecting(false); - return; - } + // try { + // const checkParams = new URLSearchParams(params); + // checkParams.set("checkOnly", "1"); + // const response = await fetch(`${base}?${checkParams.toString()}`); + // if (!response.ok) { + // const detail = (await response.text()).trim(); + // setConnectError(detail || t("sshErrorConnectionClosed")); + // setConnecting(false); + // return; + // } + // } catch { + // setConnectError(t("sshErrorWebSocket")); + // setConnecting(false); + // return; + // } let RFB: new ( target: HTMLElement, From cc498f0e33fb1f05e31e6688aeadf6d1d3df9f8d Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 10 Jun 2026 20:32:07 -0700 Subject: [PATCH 4/7] Properly paywall ui for labels --- src/components/CreateOrgLabelDialog.tsx | 10 +++++++++- src/components/EditOrgLabelDialog.tsx | 10 +++++++++- src/components/OrgLabelForm.tsx | 16 +++++++++++----- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/components/CreateOrgLabelDialog.tsx b/src/components/CreateOrgLabelDialog.tsx index 047e6289f..dfd883522 100644 --- a/src/components/CreateOrgLabelDialog.tsx +++ b/src/components/CreateOrgLabelDialog.tsx @@ -1,8 +1,10 @@ "use client"; import { useEnvContext } from "@app/hooks/useEnvContext"; +import { usePaidStatus } from "@app/hooks/usePaidStatus"; import { toast } from "@app/hooks/useToast"; import { createApiClient, formatAxiosError } from "@app/lib/api"; +import { tierMatrix } from "@server/lib/billing/tierMatrix"; import type { CreateOrEditLabelResponse } from "@server/routers/labels/types"; import type { AxiosResponse } from "axios"; import { useTranslations } from "next-intl"; @@ -18,6 +20,7 @@ import { CredenzaTitle } from "./Credenza"; import { OrgLabelForm } from "./OrgLabelForm"; +import { PaidFeaturesAlert } from "./PaidFeaturesAlert"; import { Button } from "./ui/button"; export type CreateOrgLabelDialogProps = { @@ -35,6 +38,8 @@ export function CreateOrgLabelDialog({ }: CreateOrgLabelDialogProps) { const t = useTranslations(); const api = createApiClient(useEnvContext()); + const { isPaidUser } = usePaidStatus(); + const canManageLabels = isPaidUser(tierMatrix.labels); const [isSubmitting, startTransition] = useTransition(); async function createOrgLabel(data: { name: string; color: string }) { @@ -79,8 +84,11 @@ export function CreateOrgLabelDialog({ + { + if (!canManageLabels) return; startTransition(async () => createOrgLabel(data)); }} /> @@ -98,7 +106,7 @@ export function CreateOrgLabelDialog({