various ui improvements

This commit is contained in:
miloschwartz
2026-08-13 14:54:09 -04:00
parent db716f781e
commit d9a9ae14fd
6 changed files with 102 additions and 83 deletions
+8 -8
View File
@@ -864,9 +864,9 @@
"policyAuthMethodOff": "Off",
"policyAuthSsoTitle": "Platform SSO",
"policyAuthSsoDescription": "Require sign-in through your organization's identity provider",
"policyAuthInferenceSsoDescription": "Selected users and roles can authenticate to the gateway using their identity API key",
"policyAuthInferenceIdentityKeyHelp": "Every user already has an identity API key, so you only need to create virtual API keys for non-user clients or shared access. Users can retrieve their key by signing in with their identity provider at <resourceLink></resourceLink>, where it will be shown after login.",
"policyAuthInferenceSsoDescription": "Selected users and roles can authenticate to the gateway using their identity API key.",
"policyAuthInferenceIdentityKeyHelpNoUrl": "Every user already has an identity API key, so you only need to create virtual API keys for non-user clients or shared access. Users can retrieve their key by signing in with their identity provider at this resource's URL, where it will be shown after login.",
"policyAuthInferenceIdentityKeyHelp": "Every user already has an identity API key, so you only need to create virtual API keys for non-user clients or shared access. Users can retrieve their key by signing in with their identity provider at <resourceLink></resourceLink>, where it will be shown after login.",
"policyAuthSsoSummary": "{idp} · {users} users, {roles} roles",
"policyAuthSsoDefaultIdp": "Default provider",
"policyAuthAddDefaultIdentityProvider": "Add Default Identity Provider",
@@ -1821,7 +1821,7 @@
"aiProviderBudgetUnitUsd": "USD",
"aiProviderBudgetUnitTokens": "Tokens",
"aiProviderEnabled": "Enabled",
"aiProviderEnabledDescription": "Disable to stop using this provider without deleting it",
"aiProviderEnabledDescription": "Fully disable this provider across all resources",
"aiProviderErrorCreate": "Failed to create AI provider",
"aiProviderErrorUpdate": "Failed to update AI provider",
"aiProviderErrorDelete": "Failed to delete AI provider",
@@ -1868,11 +1868,11 @@
"aiProviderModelsAllow": "Allow List",
"aiProviderModelsAllowDescription": "Models that may be used through this provider. Empty means deny all.",
"aiProviderModelsAllowPlaceholder": "Enter model key",
"aiProviderModelsAllowEmpty": "No models allowed. All requests will be denied.",
"aiProviderModelsAllowEmpty": "No models added yet.",
"aiProviderModelsBlock": "Block List",
"aiProviderModelsBlockDescription": "Models to deny even if they match an allow entry.",
"aiProviderModelsBlockPlaceholder": "Enter model key",
"aiProviderModelsBlockEmpty": "No blocked models.",
"aiProviderModelsBlockEmpty": "No models added yet.",
"aiProviderModelsAdd": "Add Models",
"aiProviderModelsClearAll": "Clear All",
"aiProviderModelsAddCustom": "Add \"{key}\"",
@@ -1938,7 +1938,7 @@
"aiResourceProviderAllowModelsEmpty": "No models found",
"aiResourceProviderAllowModelsHelp": "Only models from this provider's allow list can be selected.",
"aiResourceAliasRequired": "Alias is required for inference resources",
"aiResourceDomainConfiguration": "Domain configuration",
"aiResourceDomainConfiguration": "Domain Configuration",
"aiResourceDomainConfigurationDescription": "Choose the domain clients will use to reach this inference resource.",
"aiUsageAnalyticsTitle": "AI Usage Analytics",
"aiUsageAnalyticsDescription": "Analyze AI gateway cost, token usage, and activity across providers, resources, roles, and users",
@@ -3720,9 +3720,9 @@
"internalResourceFormMultiSiteRoutingHelp": "Selecting multiple sites enables resilient routing and failover for high availability.",
"internalResourceFormMultiSiteRoutingHelpLearnMore": "Learn more",
"editInternalResourceDialogPortRestrictionsDescription": "Restrict access to specific TCP/UDP ports or allow/block all ports.",
"createInternalResourceDialogHttpConfiguration": "HTTP configuration",
"createInternalResourceDialogHttpConfiguration": "Domain Configuration",
"createInternalResourceDialogHttpConfigurationDescription": "Choose the domain clients will use to reach this resource over HTTP or HTTPS.",
"editInternalResourceDialogHttpConfiguration": "HTTP configuration",
"editInternalResourceDialogHttpConfiguration": "Domain Configuration",
"editInternalResourceDialogHttpConfigurationDescription": "Choose the domain clients will use to reach this resource over HTTP or HTTPS.",
"editInternalResourceDialogTcp": "TCP",
"editInternalResourceDialogUdp": "UDP",
@@ -271,14 +271,14 @@ export default function CreateAiProviderPage() {
value
)
);
const currentName =
form.getValues(
"name"
);
if (
value !==
"custom"
) {
const currentName =
form.getValues(
"name"
);
const previousLabel =
t(
aiProviderTypeLabelMap[
@@ -305,6 +305,30 @@ export default function CreateAiProviderPage() {
);
targetsRef.current =
[];
} else {
const isDefaultName =
Object.entries(
aiProviderTypeLabelMap
).some(
([
type,
key
]) =>
type !==
"custom" &&
currentName ===
t(
key
)
);
if (
isDefaultName
) {
form.setValue(
"name",
""
);
}
}
}}
/>
@@ -86,6 +86,13 @@ export type ProxyResourceTargetsFormHandle = {
save: (options?: { silent?: boolean }) => Promise<boolean>;
};
const DEFAULT_ALLOWED_METHODS: ("http" | "https" | "h2c")[] = [
"http",
"https",
"h2c"
];
const EMPTY_TARGETS: LocalTarget[] = [];
type ProxyResourceTargetsFormProps = {
orgId: string;
isHttp: boolean;
@@ -115,12 +122,12 @@ export const ProxyResourceTargetsForm = forwardRef<
{
orgId,
isHttp,
initialTargets = [],
initialTargets = EMPTY_TARGETS,
resource,
providerId,
updateResource,
onChange,
allowedMethods = ["http", "https", "h2c"],
allowedMethods = DEFAULT_ALLOWED_METHODS,
emptyMessage,
embedded = false,
hideSaveButton = false,
@@ -266,7 +273,7 @@ export const ProxyResourceTargetsForm = forwardRef<
);
});
},
[sites]
[]
);
const openHealthCheckDialog = useCallback((target: LocalTarget) => {
@@ -616,7 +623,6 @@ export const ProxyResourceTargetsForm = forwardRef<
}, [
isAdvancedMode,
isHttp,
sites,
updateTarget,
getDockerStateForSite,
refreshContainersForSite,
+54 -66
View File
@@ -350,28 +350,24 @@ export function AiProviderModelListEditor({
return (
<div className="flex flex-col gap-3">
<div>
<div className="relative">
<div
ref={gridRef}
className={cn(
"grid grid-cols-1 gap-2 sm:grid-cols-2 xl:grid-cols-3",
isCollapsed && "overflow-hidden"
)}
style={
isCollapsed && clipHeight != null
? { maxHeight: clipHeight }
: undefined
}
>
{items.length === 0 ? (
<div className="flex min-w-0 items-center justify-center rounded-md border border-dashed border-input px-2.5 py-2">
<span className="text-xs text-muted-foreground">
{emptyMessage}
</span>
</div>
) : (
items.map((item) => (
{items.length === 0 ? (
<p className="text-sm text-muted-foreground">{emptyMessage}</p>
) : (
<div>
<div className="relative">
<div
ref={gridRef}
className={cn(
"grid grid-cols-1 gap-2 sm:grid-cols-2 xl:grid-cols-3",
isCollapsed && "overflow-hidden"
)}
style={
isCollapsed && clipHeight != null
? { maxHeight: clipHeight }
: undefined
}
>
{items.map((item) => (
<ModelCard
key={item.clientId}
item={item}
@@ -385,42 +381,42 @@ export function AiProviderModelListEditor({
}
onRemove={() => removeModel(item.clientId)}
/>
))
)}
))}
</div>
{isCollapsed ? (
<div className="pointer-events-none absolute inset-x-0 bottom-0 h-14 bg-gradient-to-t from-card from-25% via-card/80 to-transparent" />
) : null}
</div>
{isCollapsed ? (
<div className="pointer-events-none absolute inset-x-0 bottom-0 h-14 bg-gradient-to-t from-card from-25% via-card/80 to-transparent" />
<div className="relative z-10 flex justify-center pt-2">
<Button
type="button"
variant="text"
size="sm"
className="bg-card px-2 text-muted-foreground hover:text-foreground"
onClick={() => setListExpanded(true)}
>
{t("aiProviderModelsViewMore", {
count: items.length - collapsedLimit
})}
</Button>
</div>
) : null}
{hasOverflow && listExpanded ? (
<div className="flex justify-center pt-1">
<Button
type="button"
variant="text"
size="sm"
className="text-muted-foreground hover:text-foreground"
onClick={() => setListExpanded(false)}
>
{t("aiProviderModelsViewLess")}
</Button>
</div>
) : null}
</div>
{isCollapsed ? (
<div className="relative z-10 flex justify-center pt-2">
<Button
type="button"
variant="text"
size="sm"
className="bg-card px-2 text-muted-foreground hover:text-foreground"
onClick={() => setListExpanded(true)}
>
{t("aiProviderModelsViewMore", {
count: items.length - collapsedLimit
})}
</Button>
</div>
) : null}
{hasOverflow && listExpanded ? (
<div className="flex justify-center pt-1">
<Button
type="button"
variant="text"
size="sm"
className="text-muted-foreground hover:text-foreground"
onClick={() => setListExpanded(false)}
>
{t("aiProviderModelsViewLess")}
</Button>
</div>
) : null}
</div>
)}
<div className="flex flex-wrap items-center gap-2">
<Popover
@@ -828,9 +824,7 @@ function EditModelCredenza({
defaultValues: { modelKey: item.modelKey }
});
const [pendingBudgetRows, setPendingBudgetRows] = useState<BudgetRow[]>(
[]
);
const [pendingBudgetRows, setPendingBudgetRows] = useState<BudgetRow[]>([]);
const [attemptedBudgetsSave, setAttemptedBudgetsSave] = useState(false);
const [savingBudgets, setSavingBudgets] = useState(false);
@@ -929,9 +923,7 @@ function EditModelCredenza({
items={[
{ title: t("general"), href: "#" },
{
title: t(
"aiProviderModelsBudgetTab"
),
title: t("aiProviderModelsBudgetTab"),
href: "#"
}
]}
@@ -970,9 +962,7 @@ function EditModelCredenza({
</p>
<BudgetRowsFields
rows={pendingBudgetRows}
onChange={
setPendingBudgetRows
}
onChange={setPendingBudgetRows}
disabled={
budgetsQuery.isLoading ||
savingBudgets
@@ -984,9 +974,7 @@ function EditModelCredenza({
</>
) : (
<p className="text-sm text-muted-foreground">
{t(
"aiProviderModelsBudgetUnsaved"
)}
{t("aiProviderModelsBudgetUnsaved")}
</p>
)}
</div>
@@ -573,7 +573,6 @@ export function PolicyAuthStackSectionEdit({
className="text-primary hover:underline"
>
{inferenceResourceUrl}
<ExternalLink className="ml-1 inline size-3.5 shrink-0 align-text-bottom" />
</a>
)
}
@@ -142,6 +142,8 @@ export function ResourceTargetAddressItem({
isHttp,
allowedMethods = ["http", "https", "h2c"]
}: ResourceTargetAddressItemProps) {
"use no memo";
return (
<div className="flex items-center w-full" key={proxyTarget.targetId}>
<div className="flex items-center w-full justify-start py-0 space-x-2 px-0 cursor-default border border-input rounded-md">