From d7bff686c0a9b7f2b25d3b3e96a4c8c2853f29a4 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Fri, 14 Aug 2026 12:23:50 -0400 Subject: [PATCH 1/2] add help banner to providers and virtual api keys --- messages/en-US.json | 5 +++ .../[orgId]/settings/ai-providers/page.tsx | 3 ++ .../settings/virtual-api-keys/page.tsx | 3 ++ src/components/AiProvidersBanner.tsx | 21 +++++++++ src/components/VirtualApiKeysBanner.tsx | 45 +++++++++++++++++++ 5 files changed, 77 insertions(+) create mode 100644 src/components/AiProvidersBanner.tsx create mode 100644 src/components/VirtualApiKeysBanner.tsx diff --git a/messages/en-US.json b/messages/en-US.json index 52458fcbe..b58ebfd0c 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1678,6 +1678,9 @@ "commandVirtualApiKeys": "Virtual API Keys", "virtualApiKeysTitle": "Manage Virtual API Keys", "virtualApiKeysDescription": "Create and manage manual API keys for AI Gateway access to public AI gateways", + "virtualApiKeysBannerTitle": "Identity Keys for Every User", + "virtualApiKeysBannerDescription": "Every user already has an identity key available at {keysUrl}. You can also manually generate keys here that grant direct access to public AI gateways.", + "virtualApiKeysBannerButtonText": "View Identity Keys", "virtualApiKeys": "Virtual API Keys", "virtualApiKeysSearch": "Search keys...", "virtualApiKeysCreate": "Create Virtual API Key", @@ -1742,6 +1745,8 @@ "myVirtualApiKeysViewSecretDescription": "This secret authenticates you to AI Gateway resources", "aiProvidersTitle": "AI Providers", "aiProvidersDescription": "Connect model providers for AI workloads in this organization", + "aiProvidersBannerTitle": "Connect Model Providers", + "aiProvidersBannerDescription": "Providers are the model backends Pangolin uses for AI workloads. Connect OpenAI, Anthropic, and other providers here, then attach them to public AI gateways so users can call models with identity-aware access, budgets, and logging.", "aiProvidersAdd": "Add Provider", "aiProvidersSearch": "Search providers...", "aiProvidersEmpty": "No AI providers yet", diff --git a/src/app/[orgId]/settings/ai-providers/page.tsx b/src/app/[orgId]/settings/ai-providers/page.tsx index 024170590..572ce90d4 100644 --- a/src/app/[orgId]/settings/ai-providers/page.tsx +++ b/src/app/[orgId]/settings/ai-providers/page.tsx @@ -1,3 +1,4 @@ +import AiProvidersBanner from "@app/components/AiProvidersBanner"; import AiProvidersTable from "@app/components/AiProvidersTable"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; import { internal } from "@app/lib/api"; @@ -49,6 +50,8 @@ export default async function AiProvidersPage({ params, searchParams }: Props) { description={t("aiProvidersDescription")} /> + + ({ diff --git a/src/app/[orgId]/settings/virtual-api-keys/page.tsx b/src/app/[orgId]/settings/virtual-api-keys/page.tsx index ba8f4ea01..28a73253e 100644 --- a/src/app/[orgId]/settings/virtual-api-keys/page.tsx +++ b/src/app/[orgId]/settings/virtual-api-keys/page.tsx @@ -6,6 +6,7 @@ import { redirect } from "next/navigation"; import { cache } from "react"; import { GetOrgResponse } from "@server/routers/org"; import OrgProvider from "@app/providers/OrgProvider"; +import VirtualApiKeysBanner from "@app/components/VirtualApiKeysBanner"; import VirtualApiKeysTable, { type VirtualApiKeyRow } from "@app/components/VirtualApiKeysTable"; @@ -142,6 +143,8 @@ export default async function VirtualApiKeysPage( description={t("virtualApiKeysDescription")} /> + + { + const t = useTranslations(); + + return ( + } + description={t("aiProvidersBannerDescription")} + /> + ); +}; + +export default AiProvidersBanner; diff --git a/src/components/VirtualApiKeysBanner.tsx b/src/components/VirtualApiKeysBanner.tsx new file mode 100644 index 000000000..dd2aa1186 --- /dev/null +++ b/src/components/VirtualApiKeysBanner.tsx @@ -0,0 +1,45 @@ +"use client"; + +import { Button } from "@app/components/ui/button"; +import { useEnvContext } from "@app/hooks/useEnvContext"; +import { ArrowRight, KeyRound } from "lucide-react"; +import { useTranslations } from "next-intl"; +import Link from "next/link"; +import DismissableBanner from "./DismissableBanner"; + +type VirtualApiKeysBannerProps = { + orgId: string; +}; + +export const VirtualApiKeysBanner = ({ orgId }: VirtualApiKeysBannerProps) => { + const t = useTranslations(); + const { env } = useEnvContext(); + + const dashboardUrl = env.app.dashboardUrl?.replace(/\/$/, "") ?? ""; + const keysUrl = dashboardUrl + ? `${dashboardUrl}/${orgId}/keys` + : `/${orgId}/keys`; + + return ( + } + description={t("virtualApiKeysBannerDescription", { keysUrl })} + > + + + + + ); +}; + +export default VirtualApiKeysBanner; From 173d4a536fc71a7bcb6e59d12efa2719721fb827 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Fri, 14 Aug 2026 13:45:52 -0400 Subject: [PATCH 2/2] visual tweaks --- messages/en-US.json | 2 +- src/app/navigation.tsx | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index b58ebfd0c..e41d06f7b 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1690,7 +1690,7 @@ "virtualApiKeysName": "Name", "virtualApiKeysDescriptionOptional": "Description (optional)", "virtualApiKeysAssociateUserOptional": "Associate User (optional)", - "virtualApiKeysAssociateUserDescription": "Associate this key with a user to track usage.", + "virtualApiKeysAssociateUserDescription": "Associate this key with a user to track usage. Once created, this key immediately grants access to the selected public AI gateways. You do not need to associate a user to manually to the resource. The key will also show up in the user's profile.", "virtualApiKeysAllResources": "All public AI gateways", "virtualApiKeysAllResourcesDescription": "Allow this key to access every public AI gateway in the organization", "virtualApiKeysSelectResources": "Public AI Gateways", diff --git a/src/app/navigation.tsx b/src/app/navigation.tsx index 2f00744c0..724da9cc2 100644 --- a/src/app/navigation.tsx +++ b/src/app/navigation.tsx @@ -20,6 +20,8 @@ import { LayoutGrid, Link as LinkIcon, Logs, + MessageSquare, + MessagesSquare, MonitorUp, Plug, ReceiptText, @@ -233,7 +235,7 @@ export const orgNavSections = ( { title: "sidebarLogsAi", href: "/{orgId}/settings/logs/ai", - icon: + icon: }, { title: "sidebarLogsAiUsage",