mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
chore(site): premium paywall for <WorkspaceProxyPage /> (#27567)
This commit is contained in:
@@ -13,6 +13,7 @@ import { cachedQuery } from "#/api/queries/util";
|
||||
import type { Region, WorkspaceProxy } from "#/api/typesGenerated";
|
||||
import { useAuthenticated } from "#/hooks/useAuthenticated";
|
||||
import { useEmbeddedMetadata } from "#/hooks/useEmbeddedMetadata";
|
||||
import { useFeatureVisibility } from "#/modules/dashboard/useFeatureVisibility";
|
||||
import { type ProxyLatencyReport, useProxyLatency } from "./useProxyLatency";
|
||||
|
||||
export type Proxies = readonly Region[] | readonly WorkspaceProxy[];
|
||||
@@ -97,6 +98,7 @@ export const ProxyProvider: FC<PropsWithChildren> = ({ children }) => {
|
||||
|
||||
const { permissions } = useAuthenticated();
|
||||
const { metadata } = useEmbeddedMetadata();
|
||||
const { workspace_proxy: workspaceProxyEnabled } = useFeatureVisibility();
|
||||
|
||||
const {
|
||||
data: proxiesResp,
|
||||
@@ -106,11 +108,15 @@ export const ProxyProvider: FC<PropsWithChildren> = ({ children }) => {
|
||||
} = useQuery(
|
||||
cachedQuery({
|
||||
metadata: metadata.regions,
|
||||
queryKey: ["get-proxies"],
|
||||
queryKey: ["get-proxies", workspaceProxyEnabled],
|
||||
queryFn: async (): Promise<readonly Region[]> => {
|
||||
const apiCall = permissions.editWorkspaceProxies
|
||||
? API.getWorkspaceProxies
|
||||
: API.getWorkspaceProxyRegions;
|
||||
// Detailed proxy status requires the workspace_proxy entitlement.
|
||||
// Fall back to regions when unlicensed so admins do not hit a
|
||||
// Premium feature error on every page load.
|
||||
const apiCall =
|
||||
permissions.editWorkspaceProxies && workspaceProxyEnabled
|
||||
? API.getWorkspaceProxies
|
||||
: API.getWorkspaceProxyRegions;
|
||||
|
||||
const resp = await apiCall();
|
||||
return resp.regions;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { FC } from "react";
|
||||
import { useProxy } from "#/contexts/ProxyContext";
|
||||
import { useFeatureVisibility } from "#/modules/dashboard/useFeatureVisibility";
|
||||
import { WorkspaceProxyView } from "./WorkspaceProxyView";
|
||||
|
||||
const WorkspaceProxyPage: FC = () => {
|
||||
@@ -11,6 +12,7 @@ const WorkspaceProxyPage: FC = () => {
|
||||
isLoading: proxiesLoading,
|
||||
proxy,
|
||||
} = useProxy();
|
||||
const { workspace_proxy: workspaceProxyEnabled } = useFeatureVisibility();
|
||||
|
||||
return (
|
||||
<WorkspaceProxyView
|
||||
@@ -20,6 +22,7 @@ const WorkspaceProxyPage: FC = () => {
|
||||
hasLoaded={proxiesFetched}
|
||||
getWorkspaceProxiesError={proxiesError}
|
||||
preferredProxy={proxy.proxy}
|
||||
showPaywall={!workspaceProxyEnabled}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -11,6 +11,9 @@ import { WorkspaceProxyView } from "./WorkspaceProxyView";
|
||||
const meta: Meta<typeof WorkspaceProxyView> = {
|
||||
title: "pages/UserSettingsPage/WorkspaceProxyView",
|
||||
component: WorkspaceProxyView,
|
||||
args: {
|
||||
showPaywall: false,
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
@@ -36,6 +39,13 @@ export const Example: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const Paywall: Story = {
|
||||
args: {
|
||||
...Example.args,
|
||||
showPaywall: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const Loading: Story = {
|
||||
args: {
|
||||
...Example.args,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import type { FC } from "react";
|
||||
import type { Region } from "#/api/typesGenerated";
|
||||
import { ErrorAlert } from "#/components/Alert/ErrorAlert";
|
||||
import { PaywallPremium } from "#/components/Paywall/PaywallPremium";
|
||||
import {
|
||||
SettingsHeader,
|
||||
SettingsHeaderDescription,
|
||||
SettingsHeaderDocsLink,
|
||||
SettingsHeaderTitle,
|
||||
} from "#/components/SettingsHeader/SettingsHeader";
|
||||
import {
|
||||
@@ -16,6 +18,7 @@ import {
|
||||
import { TableEmpty } from "#/components/TableEmpty/TableEmpty";
|
||||
import { TableLoader } from "#/components/TableLoader/TableLoader";
|
||||
import type { ProxyLatencyReport } from "#/contexts/useProxyLatency";
|
||||
import { docs } from "#/utils/docs";
|
||||
import { ProxyRow } from "./WorkspaceProxyRow";
|
||||
|
||||
interface WorkspaceProxyViewProps {
|
||||
@@ -26,6 +29,7 @@ interface WorkspaceProxyViewProps {
|
||||
hasLoaded: boolean;
|
||||
preferredProxy?: Region;
|
||||
selectProxyError?: unknown;
|
||||
showPaywall: boolean;
|
||||
}
|
||||
|
||||
export const WorkspaceProxyView: FC<WorkspaceProxyViewProps> = ({
|
||||
@@ -35,10 +39,17 @@ export const WorkspaceProxyView: FC<WorkspaceProxyViewProps> = ({
|
||||
isLoading,
|
||||
hasLoaded,
|
||||
selectProxyError,
|
||||
showPaywall,
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<SettingsHeader>
|
||||
<SettingsHeader
|
||||
actions={
|
||||
<SettingsHeaderDocsLink
|
||||
href={docs("/admin/networking/workspace-proxies")}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<SettingsHeaderTitle>Workspace Proxies</SettingsHeaderTitle>
|
||||
<SettingsHeaderDescription>
|
||||
Workspace proxies improve terminal and web app connections to
|
||||
@@ -46,28 +57,38 @@ export const WorkspaceProxyView: FC<WorkspaceProxyViewProps> = ({
|
||||
</SettingsHeaderDescription>
|
||||
</SettingsHeader>
|
||||
|
||||
{Boolean(getWorkspaceProxiesError) && (
|
||||
<ErrorAlert error={getWorkspaceProxiesError} />
|
||||
)}
|
||||
{Boolean(selectProxyError) && <ErrorAlert error={selectProxyError} />}
|
||||
{showPaywall ? (
|
||||
<PaywallPremium
|
||||
message="Workspace Proxies"
|
||||
description="Workspace proxies provide low-latency connections for geo-distributed teams. You need a Premium license to use this feature."
|
||||
documentationLink={docs("/admin/networking/workspace-proxies")}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
{Boolean(getWorkspaceProxiesError) && (
|
||||
<ErrorAlert error={getWorkspaceProxiesError} />
|
||||
)}
|
||||
{Boolean(selectProxyError) && <ErrorAlert error={selectProxyError} />}
|
||||
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-[60%]">Proxy</TableHead>
|
||||
<TableHead className="w-[20%]">Status</TableHead>
|
||||
<TableHead className="w-[20%]">Latency</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<ProxiesTableBody
|
||||
proxies={proxies}
|
||||
proxyLatencies={proxyLatencies}
|
||||
isLoading={isLoading}
|
||||
hasLoaded={hasLoaded}
|
||||
/>
|
||||
</TableBody>
|
||||
</Table>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-[60%]">Proxy</TableHead>
|
||||
<TableHead className="w-[20%]">Status</TableHead>
|
||||
<TableHead className="w-[20%]">Latency</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<ProxiesTableBody
|
||||
proxies={proxies}
|
||||
proxyLatencies={proxyLatencies}
|
||||
isLoading={isLoading}
|
||||
hasLoaded={hasLoaded}
|
||||
/>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user