mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
fix: resolve entitlement check on ai bridge settings/view (#22385)
Resolves cases where the user is entitled to AI Governance but we don't show them the page because its not enabled. If for some reason the user doesn't have AI Bridge enabled anymore but still wants to access the old logs page they now can. Furthermore, we link to the docs regardless of if they have AI Bridge enabled, this is inline with our other settings pages.
This commit is contained in:
@@ -3,7 +3,7 @@ import { useFilter } from "components/Filter/Filter";
|
||||
import { useUserFilterMenu } from "components/Filter/UserFilter";
|
||||
import { useAuthenticated } from "hooks";
|
||||
import { usePaginatedQuery } from "hooks/usePaginatedQuery";
|
||||
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
|
||||
import { useDashboard } from "modules/dashboard/useDashboard";
|
||||
import { RequirePermission } from "modules/permissions/RequirePermission";
|
||||
import type { FC } from "react";
|
||||
import { useSearchParams } from "react-router";
|
||||
@@ -13,14 +13,16 @@ import { useProviderFilterMenu } from "./RequestLogsFilter/ProviderFilter";
|
||||
import { RequestLogsPageView } from "./RequestLogsPageView";
|
||||
|
||||
const RequestLogsPage: FC = () => {
|
||||
const feats = useFeatureVisibility();
|
||||
const { permissions } = useAuthenticated();
|
||||
const { entitlements } = useDashboard();
|
||||
|
||||
// Users are allowed to view their own request logs via the API,
|
||||
// but this page is only visible if the feature is enabled and the user
|
||||
// has the `viewAnyAIBridgeInterception` permission.
|
||||
// (as its defined in the Admin settings dropdown).
|
||||
const isEntitled = Boolean(feats.aibridge);
|
||||
const isEntitled =
|
||||
entitlements.features.aibridge.entitlement === "entitled" ||
|
||||
entitlements.features.aibridge.entitlement === "grace_period";
|
||||
const hasPermission = permissions.viewAnyAIBridgeInterception;
|
||||
const canViewRequestLogs = isEntitled && hasPermission;
|
||||
|
||||
@@ -68,7 +70,7 @@ const RequestLogsPage: FC = () => {
|
||||
|
||||
<RequestLogsPageView
|
||||
isLoading={interceptionsQuery.isLoading}
|
||||
isRequestLogsVisible={isEntitled}
|
||||
isRequestLogsEntitled={isEntitled}
|
||||
interceptions={interceptionsQuery.data?.results}
|
||||
interceptionsQuery={interceptionsQuery}
|
||||
filterProps={{
|
||||
|
||||
@@ -47,13 +47,13 @@ type Story = StoryObj<typeof RequestLogsPageView>;
|
||||
|
||||
export const Paywall: Story = {
|
||||
args: {
|
||||
isRequestLogsVisible: false,
|
||||
isRequestLogsEntitled: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const Loaded: Story = {
|
||||
args: {
|
||||
isRequestLogsVisible: true,
|
||||
isRequestLogsEntitled: true,
|
||||
interceptions,
|
||||
filterProps: {
|
||||
...defaultFilterProps,
|
||||
@@ -64,7 +64,7 @@ export const Loaded: Story = {
|
||||
|
||||
export const Empty: Story = {
|
||||
args: {
|
||||
isRequestLogsVisible: true,
|
||||
isRequestLogsEntitled: true,
|
||||
interceptions: [],
|
||||
filterProps: {
|
||||
...defaultFilterProps,
|
||||
@@ -76,7 +76,7 @@ export const Empty: Story = {
|
||||
export const Loading: Story = {
|
||||
args: {
|
||||
isLoading: true,
|
||||
isRequestLogsVisible: true,
|
||||
isRequestLogsEntitled: true,
|
||||
interceptions: [],
|
||||
filterProps: {
|
||||
...defaultFilterProps,
|
||||
|
||||
@@ -19,7 +19,7 @@ import { RequestLogsRow } from "./RequestLogsRow/RequestLogsRow";
|
||||
|
||||
interface RequestLogsPageViewProps {
|
||||
isLoading: boolean;
|
||||
isRequestLogsVisible: boolean;
|
||||
isRequestLogsEntitled: boolean;
|
||||
interceptions?: readonly AIBridgeInterception[];
|
||||
interceptionsQuery: PaginationResult;
|
||||
filterProps: ComponentProps<typeof RequestLogsFilter>;
|
||||
@@ -27,12 +27,12 @@ interface RequestLogsPageViewProps {
|
||||
|
||||
export const RequestLogsPageView: FC<RequestLogsPageViewProps> = ({
|
||||
isLoading,
|
||||
isRequestLogsVisible,
|
||||
isRequestLogsEntitled,
|
||||
interceptions,
|
||||
interceptionsQuery,
|
||||
filterProps,
|
||||
}) => {
|
||||
if (!isRequestLogsVisible) {
|
||||
if (!isRequestLogsEntitled) {
|
||||
return <PaywallAIGovernance />;
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -14,7 +14,10 @@ const AIGovernanceSettingsPage: FC = () => {
|
||||
|
||||
<AIGovernanceSettingsPageView
|
||||
options={deploymentConfig.options}
|
||||
featureAIBridgeEnabled={entitlements.features.aibridge.enabled}
|
||||
featureAIBridgeEntitled={
|
||||
entitlements.features.aibridge.entitlement === "entitled" ||
|
||||
entitlements.features.aibridge.entitlement === "grace_period"
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
+2
-2
@@ -23,7 +23,7 @@ const meta: Meta<typeof AIGovernanceSettingsPageView> = {
|
||||
hidden: false,
|
||||
},
|
||||
],
|
||||
featureAIBridgeEnabled: true,
|
||||
featureAIBridgeEntitled: true,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -34,7 +34,7 @@ export const Page: Story = {};
|
||||
|
||||
export const Paywall: Story = {
|
||||
args: {
|
||||
featureAIBridgeEnabled: false,
|
||||
featureAIBridgeEntitled: false,
|
||||
options: [],
|
||||
},
|
||||
};
|
||||
|
||||
+4
-4
@@ -14,19 +14,18 @@ import OptionsTable from "../OptionsTable";
|
||||
|
||||
type AIGovernanceSettingsPageViewProps = {
|
||||
options: SerpentOption[];
|
||||
featureAIBridgeEnabled: boolean;
|
||||
featureAIBridgeEntitled: boolean;
|
||||
};
|
||||
|
||||
export const AIGovernanceSettingsPageView: FC<
|
||||
AIGovernanceSettingsPageViewProps
|
||||
> = ({ options, featureAIBridgeEnabled }) => {
|
||||
> = ({ options, featureAIBridgeEntitled }) => {
|
||||
return (
|
||||
<Stack direction="column" spacing={6}>
|
||||
<SettingsHeader>
|
||||
<SettingsHeaderTitle>AI Governance</SettingsHeaderTitle>
|
||||
</SettingsHeader>
|
||||
|
||||
{featureAIBridgeEnabled ? (
|
||||
<div>
|
||||
<SettingsHeader
|
||||
actions={
|
||||
@@ -41,15 +40,16 @@ export const AIGovernanceSettingsPageView: FC<
|
||||
</SettingsHeaderDescription>
|
||||
</SettingsHeader>
|
||||
|
||||
{featureAIBridgeEntitled ? (
|
||||
<OptionsTable
|
||||
options={options
|
||||
.filter((o) => deploymentGroupHasParent(o.group, "AI Bridge"))
|
||||
.filter((o) => !o.annotations?.secret === true)}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<PaywallAIGovernance />
|
||||
)}
|
||||
</div>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user