Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import type { FC } from "react";
import { useAuthenticated } from "#/hooks/useAuthenticated";
import { useDashboard } from "#/modules/dashboard/useDashboard";
import { useFeatureVisibility } from "#/modules/dashboard/useFeatureVisibility";
import { useDeploymentConfig } from "#/modules/management/DeploymentConfigProvider";
import { pageTitle } from "#/utils/page";
import { ObservabilitySettingsPageView } from "./ObservabilitySettingsPageView";

const ObservabilitySettingsPage: FC = () => {
const { deploymentConfig } = useDeploymentConfig();
const { entitlements } = useDashboard();
const { multiple_organizations: hasPremiumLicense } = useFeatureVisibility();
const { permissions } = useAuthenticated();

return (
<>
Expand All @@ -17,7 +17,7 @@ const ObservabilitySettingsPage: FC = () => {
<ObservabilitySettingsPageView
options={deploymentConfig.options}
featureAuditLogEnabled={entitlements.features.audit_log.enabled}
isPremium={hasPremiumLicense}
canViewPremium={permissions.viewAllLicenses}
/>
</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,39 @@
import type { Meta, StoryObj } from "@storybook/react-vite";
import { expect, within } from "storybook/test";
import type { SerpentGroup } from "#/api/typesGenerated";
import { MockPermissions } from "#/testHelpers/entities";
import { ObservabilitySettingsPageView } from "./ObservabilitySettingsPageView";

const group: SerpentGroup = {
const introspectionGroup: SerpentGroup = {
name: "Introspection",
description: "",
};

const retentionGroup: SerpentGroup = {
name: "Retention",
description: "",
};

const meta: Meta<typeof ObservabilitySettingsPageView> = {
title: "pages/DeploymentSettingsPage/ObservabilitySettingsPageView",
component: ObservabilitySettingsPageView,
args: {
options: [
{
name: "Audit Logs Retention",
description:
"How long audit log entries are retained. Set to 0 to disable (keep indefinitely).",
value: 0,
group: retentionGroup,
flag: "audit-logs-retention",
env: "CODER_AUDIT_LOGS_RETENTION",
yaml: "audit_logs",
hidden: false,
},
{
name: "Verbose",
value: true,
group,
group: introspectionGroup,
flag: "verbose",
flag_shorthand: "v",
hidden: false,
Expand All @@ -40,13 +57,13 @@ const meta: Meta<typeof ObservabilitySettingsPageView> = {
description:
"Serve prometheus metrics on the address defined by prometheus address.",
value: true,
group: { ...group },
group: { ...introspectionGroup },
flag: "prometheus-enable",
hidden: false,
},
],
featureAuditLogEnabled: true,
isPremium: false,
canViewPremium: MockPermissions.viewAllLicenses,
},
};

Expand All @@ -56,38 +73,45 @@ type Story = StoryObj<typeof ObservabilitySettingsPageView>;
export const Page: Story = {};

export const OSS: Story = {
args: { featureAuditLogEnabled: false, isPremium: false },
args: { featureAuditLogEnabled: false },
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);

const alert = canvas.getByRole("alert");
await expect(alert).toBeVisible();
await expect(within(alert).getByText("Premium")).toBeVisible();
await expect(canvas.getByText("Audit Logging")).toBeVisible();
await expect(
within(alert).getByRole("link", {
name: "Read the Audit Logs documentation",
}),
canvas.getByRole("link", { name: "Start trial for free" }),
).toHaveAttribute("href", "/deployment/premium");
await expect(
canvas.getByRole("link", { name: "Learn more about premium" }),
).toBeVisible();
await expect(canvas.queryByText("Enterprise")).not.toBeInTheDocument();
await expect(
canvas.queryByText("Audit Logs Retention"),
).not.toBeInTheDocument();
},
};

export const Premium: Story = {
args: { featureAuditLogEnabled: true, isPremium: true },
export const OSSWithoutLicenseAccess: Story = {
args: { featureAuditLogEnabled: false, canViewPremium: false },
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);

await expect(canvas.queryByRole("alert")).not.toBeInTheDocument();
await expect(canvas.getByText("Premium")).toBeVisible();
await expect(
canvas.getByText(/contact your deployment administrator/i),
).toBeVisible();
await expect(
canvas.queryByRole("link", { name: "Start trial for free" }),
).not.toBeInTheDocument();
},
};

export const EnterpriseAuditLogs: Story = {
args: { featureAuditLogEnabled: true, isPremium: false },
export const Entitled: Story = {
args: { featureAuditLogEnabled: true },
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);

await expect(canvas.queryByRole("alert")).not.toBeInTheDocument();
await expect(canvas.getByText("Enterprise")).toBeVisible();
await expect(
canvas.queryByRole("link", { name: "Start trial for free" }),
).not.toBeInTheDocument();
await expect(canvas.getByText("Audit Logs Retention")).toBeVisible();
},
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import type { FC } from "react";
import type { SerpentOption } from "#/api/typesGenerated";
import { Alert } from "#/components/Alert/Alert";
import { Badges, PremiumBadge } from "#/components/Badges/Badges";
import { PaywallSmall } from "#/components/Paywall/PaywallSmall";
import { PaywallPremium } from "#/components/Paywall/PaywallPremium";
import {
SettingsHeader,
SettingsHeaderDescription,
Expand All @@ -16,12 +14,12 @@ import OptionsTable from "../OptionsTable";
type ObservabilitySettingsPageViewProps = {
options: SerpentOption[];
featureAuditLogEnabled: boolean;
isPremium: boolean;
canViewPremium: boolean;
};

export const ObservabilitySettingsPageView: FC<
ObservabilitySettingsPageViewProps
> = ({ options, featureAuditLogEnabled, isPremium }) => {
> = ({ options, featureAuditLogEnabled, canViewPremium }) => {
return (
<div className="flex flex-col gap-12">
<div>
Expand All @@ -40,30 +38,16 @@ export const ObservabilitySettingsPageView: FC<
</SettingsHeaderDescription>
</SettingsHeader>

{featureAuditLogEnabled || isPremium ? (
<Badges>{<PremiumBadge />}</Badges>
{featureAuditLogEnabled ? (
<OptionsTable
options={options.filter((o) => o.name === "Audit Logs Retention")}
/>
Comment on lines +42 to +44

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Label both options tables for screen readers

When audit logging is enabled, this newly added OptionsTable renders alongside the existing Monitoring table, but OptionsTable emits a <table> with neither an aria-label nor a caption. Both tables therefore have the same "Option" and "Value" headers and are indistinguishable to screen-reader users; extend OptionsTable to accept an accessible name and label both instances.

AGENTS.md reference: site/AGENTS.md:L191-L195

Useful? React with 👍 / 👎.

) : (
<>
<Alert severity="info">
Audit logging lets auditors monitor user operations across your
deployment. It requires a Premium license.{" "}
<a
href={docs("/admin/security/audit-logs")}
target="_blank"
rel="noreferrer"
className="text-content-link font-medium"
>
Read the Audit Logs documentation
</a>
.
</Alert>
<br />
<PaywallSmall
message="Audit Logs"
canViewPremium
description="A Premium license is required for access to Audit Log monitoring."
/>
</>
<PaywallPremium
message="Audit Logging"
description="Audit logging lets auditors monitor user operations across your deployment. You need a Premium license to use this feature."
canViewPremium={canViewPremium}
/>
)}
</div>

Expand Down
Loading