mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix: resolve <Alert /> button poor visibility (#22597)
Closes #22244 This pull-request makes our `<Alert />`'s more inline with the Figma style-system, we're looking to ensure that these are vertically rendered now and not horizontal WCAG nightmares. --------- Co-authored-by: Danielle Maywood <danielle@themaywoods.com>
This commit is contained in:
co-authored by
Danielle Maywood
parent
7dc81bdef1
commit
4a363b0d85
@@ -11,7 +11,7 @@ export default meta;
|
||||
type Story = StoryObj<typeof Alert>;
|
||||
|
||||
const ExampleAction = (
|
||||
<Button onClick={() => null} size="sm" variant="subtle">
|
||||
<Button onClick={() => null} size="sm">
|
||||
Button
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -96,31 +96,37 @@ export const Alert: FC<AlertProps> = ({
|
||||
className={cn(alertVariants({ severity, prominent }), className)}
|
||||
{...props}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-4 text-sm">
|
||||
<div
|
||||
className={cn(
|
||||
"relative flex flex-col justify-between gap-4 text-sm",
|
||||
dismissible ? "pr-8" : undefined,
|
||||
)}
|
||||
>
|
||||
<div className="flex min-w-0 flex-1 flex-row items-start gap-3">
|
||||
<Icon className={cn("size-icon-sm mt-[3px]", iconClassName)} />
|
||||
<div className="min-w-0 flex-1">{children}</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{actions}
|
||||
|
||||
{dismissible && (
|
||||
<Button
|
||||
variant="subtle"
|
||||
size="icon"
|
||||
className="!size-auto !min-w-0 !p-0"
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
onDismiss?.();
|
||||
}}
|
||||
data-testid="dismiss-banner-btn"
|
||||
aria-label="Dismiss"
|
||||
>
|
||||
<XIcon className="!p-0" />
|
||||
</Button>
|
||||
)}
|
||||
<div className="flex min-w-0 flex-1 flex-col gap-4">
|
||||
<div className="min-w-0 flex-1">{children}</div>
|
||||
{actions && (
|
||||
<div className="flex items-center gap-2">{actions}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{dismissible && (
|
||||
<Button
|
||||
variant="subtle"
|
||||
size="icon"
|
||||
className="size-8 !min-w-0 !p-0 absolute top-3 right-3"
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
onDismiss?.();
|
||||
}}
|
||||
data-testid="dismiss-banner-btn"
|
||||
aria-label="Dismiss"
|
||||
>
|
||||
<XIcon className="!p-0 size-icon-lg" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -139,5 +145,5 @@ export const AlertTitle: React.FC<React.ComponentPropsWithRef<"h2">> = ({
|
||||
className,
|
||||
...props
|
||||
}) => {
|
||||
return <h2 className={cn("m-0 text-sm font-medium", className)} {...props} />;
|
||||
return <h2 className={cn("m-0 text-sm", className)} {...props} />;
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@ export default meta;
|
||||
type Story = StoryObj<typeof ErrorAlert>;
|
||||
|
||||
const ExampleAction = (
|
||||
<Button onClick={() => null} size="sm" variant="subtle">
|
||||
<Button onClick={() => null} size="sm">
|
||||
Button
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -75,6 +75,7 @@ const ErrorScriptAlert: FC = () => {
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="mx-0"
|
||||
>
|
||||
startup script has exited with an error
|
||||
</Link>
|
||||
@@ -164,7 +165,7 @@ const TerminalAlert: FC<AlertProps> = (props) => {
|
||||
<Alert
|
||||
{...props}
|
||||
className={cn(
|
||||
"rounded-none border-0 border-b border-l-[3px] border-b-border-default bg-surface-primary mb-px [&>div]:items-center",
|
||||
"rounded-none border-0 border-b border-l-[3px] border-b-border-default bg-surface-primary mb-px",
|
||||
severityBorderColors[severity],
|
||||
)}
|
||||
/>
|
||||
@@ -192,7 +193,6 @@ const RefreshSessionButton: FC = () => {
|
||||
<Button
|
||||
disabled={isRefreshing}
|
||||
size="sm"
|
||||
variant="subtle"
|
||||
onClick={() => {
|
||||
setIsRefreshing(true);
|
||||
window.location.reload();
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { FC, FormEvent } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import TextareaAutosize from "react-textarea-autosize";
|
||||
import type * as TypesGen from "#/api/typesGenerated";
|
||||
import { Alert } from "#/components/Alert/Alert";
|
||||
import { Alert, AlertDescription } from "#/components/Alert/Alert";
|
||||
import { Button } from "#/components/Button/Button";
|
||||
import { Link } from "#/components/Link/Link";
|
||||
import { Switch } from "#/components/Switch/Switch";
|
||||
@@ -278,9 +278,11 @@ export const AgentSettingsBehaviorPageView: FC<
|
||||
/>
|
||||
{userInvisibleCharCount > 0 && (
|
||||
<Alert severity="warning">
|
||||
This text contains {userInvisibleCharCount} invisible Unicode{" "}
|
||||
{userInvisibleCharCount !== 1 ? "characters" : "character"} that
|
||||
could hide content. These will be stripped on save.
|
||||
<AlertDescription>
|
||||
This text contains {userInvisibleCharCount} invisible Unicode{" "}
|
||||
{userInvisibleCharCount !== 1 ? "characters" : "character"} that
|
||||
could hide content. These will be stripped on save.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
<div className="flex justify-end gap-2">
|
||||
@@ -375,9 +377,12 @@ export const AgentSettingsBehaviorPageView: FC<
|
||||
/>
|
||||
{systemInvisibleCharCount > 0 && (
|
||||
<Alert severity="warning">
|
||||
This text contains {systemInvisibleCharCount} invisible Unicode{" "}
|
||||
{systemInvisibleCharCount !== 1 ? "characters" : "character"}{" "}
|
||||
that could hide content. These will be stripped on save.
|
||||
<AlertDescription>
|
||||
This text contains {systemInvisibleCharCount} invisible
|
||||
Unicode{" "}
|
||||
{systemInvisibleCharCount !== 1 ? "characters" : "character"}{" "}
|
||||
that could hide content. These will be stripped on save.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
<div className="flex justify-end gap-2">
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
} from "react";
|
||||
import type * as TypesGen from "#/api/typesGenerated";
|
||||
import type { ChatMessagePart, ChatQueuedMessage } from "#/api/typesGenerated";
|
||||
import { Alert } from "#/components/Alert/Alert";
|
||||
import { Alert, AlertDescription } from "#/components/Alert/Alert";
|
||||
import { Button } from "#/components/Button/Button";
|
||||
import {
|
||||
Command,
|
||||
@@ -683,9 +683,11 @@ export const AgentChatInput: FC<AgentChatInputProps> = ({
|
||||
{invisibleCharCount > 0 && (
|
||||
<div className="px-3 pb-1">
|
||||
<Alert severity="warning">
|
||||
This message contains {invisibleCharCount} invisible Unicode
|
||||
character{invisibleCharCount !== 1 ? "s" : ""} that could hide
|
||||
content. Review carefully before sending.
|
||||
<AlertDescription>
|
||||
This message contains {invisibleCharCount} invisible Unicode
|
||||
character{invisibleCharCount !== 1 ? "s" : ""} that could hide
|
||||
content. Review carefully before sending.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Link } from "react-router";
|
||||
import { toast } from "sonner";
|
||||
import { isApiError } from "#/api/errors";
|
||||
import type * as TypesGen from "#/api/typesGenerated";
|
||||
import { Alert } from "#/components/Alert/Alert";
|
||||
import { Alert, AlertDescription } from "#/components/Alert/Alert";
|
||||
import { ErrorAlert } from "#/components/Alert/ErrorAlert";
|
||||
import { Button } from "#/components/Button/Button";
|
||||
import { useDashboard } from "#/modules/dashboard/useDashboard";
|
||||
@@ -320,14 +320,15 @@ export const AgentCreateForm: FC<AgentCreateFormProps> = ({
|
||||
isUsageLimitData(createError.response.data) ? (
|
||||
<Alert
|
||||
severity="info"
|
||||
className="py-2"
|
||||
actions={
|
||||
<Button asChild variant="subtle" size="sm">
|
||||
<Button asChild size="sm">
|
||||
<Link to="/agents/analytics">View Usage</Link>
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
{formatUsageLimitMessage(createError.response.data)}
|
||||
<AlertDescription>
|
||||
{formatUsageLimitMessage(createError.response.data)}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
) : (
|
||||
<ErrorAlert error={createError} />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { FC } from "react";
|
||||
import { Alert } from "#/components/Alert/Alert";
|
||||
import { Alert, AlertDescription, AlertTitle } from "#/components/Alert/Alert";
|
||||
import { Button } from "#/components/Button/Button";
|
||||
import { Link } from "#/components/Link/Link";
|
||||
import { docs } from "#/utils/docs";
|
||||
@@ -12,28 +12,21 @@ export const ChatAccessDeniedAlert: FC = () => {
|
||||
return (
|
||||
<Alert
|
||||
severity="info"
|
||||
className="py-2"
|
||||
actions={
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="subtle"
|
||||
size="sm"
|
||||
onClick={() => window.location.reload()}
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
<Link href={docsLink} target="_blank" rel="noreferrer" size="sm">
|
||||
View Docs
|
||||
</Link>
|
||||
</div>
|
||||
<Button size="sm" onClick={() => window.location.reload()}>
|
||||
Refresh
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<p className="m-0 font-medium">Permission required</p>
|
||||
<p className="m-0 mt-1 text-sm text-content-secondary">
|
||||
<AlertTitle>Permission required</AlertTitle>
|
||||
<AlertDescription>
|
||||
You don't have permission to use Coder Agents. Contact your Coder
|
||||
administrator for access. Refresh this page after access has been
|
||||
granted.
|
||||
</p>
|
||||
granted.{" "}
|
||||
<Link href={docsLink} target="_blank" rel="noreferrer">
|
||||
View Docs
|
||||
</Link>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { ExternalLinkIcon } from "lucide-react";
|
||||
import { type FC, useEffect, useState } from "react";
|
||||
import { Alert, AlertDescription, AlertTitle } from "#/components/Alert/Alert";
|
||||
import { Button } from "#/components/Button/Button";
|
||||
import { Pill } from "#/components/Pill/Pill";
|
||||
import { Link } from "#/components/Link/Link";
|
||||
import { Response, Shimmer } from "../ChatElements";
|
||||
import { getKindLabel, getProviderStatusURL } from "./chatStatusHelpers";
|
||||
import { getProviderStatusURL } from "./chatStatusHelpers";
|
||||
import type { LiveStatusModel } from "./liveStatusModel";
|
||||
|
||||
const RESPONSE_STARTUP_GRACE_MS = 15_000;
|
||||
@@ -116,76 +114,58 @@ const StatusCountdown: FC<{
|
||||
|
||||
const StatusAlert: FC<{ status: RetryOrFailedStatus }> = ({ status }) => {
|
||||
const statusURL = getProviderStatusURL(status.kind, status.provider);
|
||||
const pillType =
|
||||
status.phase === "failed"
|
||||
? "error"
|
||||
: status.kind === "generic"
|
||||
? "inactive"
|
||||
: "warning";
|
||||
const severity =
|
||||
status.phase === "failed"
|
||||
? "error"
|
||||
: status.kind === "generic"
|
||||
? "info"
|
||||
: "warning";
|
||||
const hasMetadata =
|
||||
status.phase === "retrying" ||
|
||||
(status.phase === "failed" && status.statusCode !== undefined);
|
||||
const metadataItems: React.ReactNode[] = [];
|
||||
if (status.phase === "retrying" && status.retryingAt) {
|
||||
metadataItems.push(
|
||||
<StatusCountdown
|
||||
key="countdown"
|
||||
deadline={status.retryingAt}
|
||||
label="Retrying in"
|
||||
/>,
|
||||
);
|
||||
}
|
||||
if (status.phase === "retrying") {
|
||||
metadataItems.push(<span key="attempt">Attempt {status.attempt}</span>);
|
||||
}
|
||||
if (status.phase === "failed" && status.statusCode !== undefined) {
|
||||
metadataItems.push(<span key="code">HTTP {status.statusCode}</span>);
|
||||
}
|
||||
|
||||
return (
|
||||
<Alert
|
||||
severity={severity}
|
||||
className="py-3"
|
||||
actions={
|
||||
statusURL && (
|
||||
<Button asChild variant="subtle" size="sm">
|
||||
<a href={statusURL} target="_blank" rel="noreferrer">
|
||||
Status
|
||||
<ExternalLinkIcon />
|
||||
</a>
|
||||
</Button>
|
||||
)
|
||||
metadataItems.length > 0 ? (
|
||||
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-content-secondary">
|
||||
{metadataItems}
|
||||
</div>
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
<div className="space-y-2.5">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<AlertTitle>{status.title}</AlertTitle>
|
||||
<Pill
|
||||
className="h-5 px-2.5 text-[10px] font-semibold"
|
||||
type={pillType}
|
||||
>
|
||||
{getKindLabel(status.kind)}
|
||||
</Pill>
|
||||
</div>
|
||||
<AlertDescription>{status.message}</AlertDescription>
|
||||
{hasMetadata && (
|
||||
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-content-secondary">
|
||||
{status.phase === "retrying" && status.retryingAt && (
|
||||
<StatusCountdown
|
||||
deadline={status.retryingAt}
|
||||
label="Retrying in"
|
||||
/>
|
||||
)}
|
||||
{status.phase === "retrying" && (
|
||||
<span>Attempt {status.attempt}</span>
|
||||
)}
|
||||
|
||||
{status.phase === "failed" && status.statusCode !== undefined && (
|
||||
<span>HTTP {status.statusCode}</span>
|
||||
)}
|
||||
</div>
|
||||
<AlertTitle>{status.title}</AlertTitle>
|
||||
<AlertDescription>
|
||||
{status.message}{" "}
|
||||
{statusURL && (
|
||||
<Link href={statusURL} target="_blank" rel="noreferrer">
|
||||
Status
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
||||
const ReconnectingAlert: FC<{ status: ReconnectingStatus }> = ({ status }) => {
|
||||
return (
|
||||
<Alert severity="info" className="py-3">
|
||||
<div className="space-y-2.5">
|
||||
<AlertTitle>{status.title}</AlertTitle>
|
||||
<AlertDescription>{status.message}</AlertDescription>
|
||||
<Alert
|
||||
severity="info"
|
||||
actions={
|
||||
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-content-secondary">
|
||||
<StatusCountdown
|
||||
deadline={status.retryingAt}
|
||||
@@ -193,7 +173,10 @@ const ReconnectingAlert: FC<{ status: ReconnectingStatus }> = ({ status }) => {
|
||||
/>
|
||||
<span>Attempt {status.attempt}</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<AlertTitle>{status.title}</AlertTitle>
|
||||
<AlertDescription>{status.message}</AlertDescription>
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -92,7 +92,6 @@ export const TerminalOverloadedError: Story = {
|
||||
expect(
|
||||
canvas.getByRole("heading", { name: /service overloaded/i }),
|
||||
).toBeVisible();
|
||||
expect(canvas.getByText("Overloaded")).toBeVisible();
|
||||
expect(
|
||||
canvas.getByText(/anthropic is currently overloaded./i),
|
||||
).toBeVisible();
|
||||
@@ -122,7 +121,6 @@ export const TerminalStartupTimeoutError: Story = {
|
||||
expect(
|
||||
canvas.getByRole("heading", { name: /startup timed out/i }),
|
||||
).toBeVisible();
|
||||
expect(canvas.getByText("Startup timeout")).toBeVisible();
|
||||
expect(
|
||||
canvas.getByText(/anthropic did not start responding in time./i),
|
||||
).toBeVisible();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Link } from "react-router";
|
||||
import type { UrlTransform } from "streamdown";
|
||||
import type * as TypesGen from "#/api/typesGenerated";
|
||||
import { Alert } from "#/components/Alert/Alert";
|
||||
import { Alert, AlertDescription } from "#/components/Alert/Alert";
|
||||
import { Button } from "#/components/Button/Button";
|
||||
import type { ChatDetailError } from "../../utils/usageLimitMessage";
|
||||
import { ChatStatusCallout } from "./ChatStatusCallout";
|
||||
@@ -92,14 +92,13 @@ export const LiveStreamTailContent = ({
|
||||
{usageLimitStatus ? (
|
||||
<Alert
|
||||
severity="info"
|
||||
className="py-2"
|
||||
actions={
|
||||
<Button asChild variant="subtle" size="sm">
|
||||
<Button asChild size="sm">
|
||||
<Link to="/agents/analytics">View Usage</Link>
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
{usageLimitStatus.message}
|
||||
<AlertDescription>{usageLimitStatus.message}</AlertDescription>
|
||||
</Alert>
|
||||
) : terminalStatus ? (
|
||||
<ChatStatusCallout status={terminalStatus} />
|
||||
|
||||
@@ -95,7 +95,6 @@ export const RetryWithVisibleReason: Story = {
|
||||
expect(
|
||||
canvas.getByText(/anthropic returned an unexpected error/i),
|
||||
).toBeVisible();
|
||||
expect(canvas.getByText("Unexpected error")).toBeVisible();
|
||||
expect(canvas.getByText(/attempt 1/i)).toBeVisible();
|
||||
expect(canvas.queryByText(/please try again/i)).not.toBeInTheDocument();
|
||||
expect(canvas.queryByText(/provider anthropic/i)).not.toBeInTheDocument();
|
||||
@@ -125,7 +124,6 @@ export const RetryRateLimited: Story = {
|
||||
expect(
|
||||
canvas.getByText(/anthropic is rate limiting requests/i),
|
||||
).toBeVisible();
|
||||
expect(canvas.getByText("Rate limit")).toBeVisible();
|
||||
await waitFor(() => {
|
||||
expect(canvasElement.textContent).toMatch(/retrying in \d+s/i);
|
||||
});
|
||||
@@ -160,7 +158,6 @@ export const RetryInvalidTimestamp: Story = {
|
||||
expect(
|
||||
canvas.getByText(/anthropic is rate limiting requests/i),
|
||||
).toBeVisible();
|
||||
expect(canvas.getByText("Rate limit")).toBeVisible();
|
||||
expect(canvas.getByText(/attempt 3/i)).toBeVisible();
|
||||
await waitFor(() => {
|
||||
expect(canvas.queryByText(/retrying in nan/i)).not.toBeInTheDocument();
|
||||
@@ -192,7 +189,6 @@ export const RetryOverloaded: Story = {
|
||||
expect(
|
||||
canvas.getByText(/anthropic is temporarily overloaded/i),
|
||||
).toBeVisible();
|
||||
expect(canvas.getByText("Overloaded")).toBeVisible();
|
||||
const statusLink = screen.getByRole("link", { name: /status/i });
|
||||
expect(statusLink).toBeVisible();
|
||||
expect(statusLink).toHaveAttribute("href", "https://status.anthropic.com");
|
||||
@@ -221,7 +217,6 @@ export const RetryTimeout: Story = {
|
||||
expect(
|
||||
canvas.getByText(/anthropic is temporarily unavailable/i),
|
||||
).toBeVisible();
|
||||
expect(canvas.getByText("Timeout")).toBeVisible();
|
||||
expect(
|
||||
canvas.queryByRole("link", { name: /status/i }),
|
||||
).not.toBeInTheDocument();
|
||||
@@ -250,7 +245,6 @@ export const RetryStartupTimeout: Story = {
|
||||
expect(
|
||||
canvas.getByText(/anthropic did not start responding in time/i),
|
||||
).toBeVisible();
|
||||
expect(canvas.getByText("Startup timeout")).toBeVisible();
|
||||
expect(canvas.queryByText(/please try again/i)).not.toBeInTheDocument();
|
||||
expect(canvas.queryByText(/provider anthropic/i)).not.toBeInTheDocument();
|
||||
expect(
|
||||
|
||||
@@ -23,19 +23,6 @@ const normalizeProvider = (provider?: string): string | undefined => {
|
||||
}
|
||||
};
|
||||
|
||||
const humanizeKind = (kind: string): string => {
|
||||
const words = kind
|
||||
.trim()
|
||||
.split(/[_\-\s]+/)
|
||||
.filter(Boolean);
|
||||
if (words.length === 0) {
|
||||
return "Unexpected error";
|
||||
}
|
||||
return words
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(" ");
|
||||
};
|
||||
|
||||
export const getErrorTitle = (
|
||||
kind: ChatProviderFailureKind | (string & {}),
|
||||
mode: "retry" | "error",
|
||||
@@ -58,29 +45,6 @@ export const getErrorTitle = (
|
||||
}
|
||||
};
|
||||
|
||||
export const getKindLabel = (
|
||||
kind: ChatProviderFailureKind | (string & {}),
|
||||
): string => {
|
||||
switch (kind) {
|
||||
case "generic":
|
||||
return "Unexpected error";
|
||||
case "overloaded":
|
||||
return "Overloaded";
|
||||
case "rate_limit":
|
||||
return "Rate limit";
|
||||
case "timeout":
|
||||
return "Timeout";
|
||||
case "startup_timeout":
|
||||
return "Startup timeout";
|
||||
case "auth":
|
||||
return "Authentication";
|
||||
case "config":
|
||||
return "Configuration";
|
||||
default:
|
||||
return humanizeKind(kind);
|
||||
}
|
||||
};
|
||||
|
||||
export const getProviderStatusURL = (
|
||||
kind: ChatProviderFailureKind | (string & {}),
|
||||
provider?: string,
|
||||
|
||||
@@ -71,7 +71,7 @@ export const NotificationEvents: FC<NotificationEventsProps> = ({
|
||||
severity="warning"
|
||||
prominent
|
||||
actions={
|
||||
<Button variant="subtle" size="sm" asChild>
|
||||
<Button size="sm" asChild>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
@@ -91,7 +91,7 @@ export const NotificationEvents: FC<NotificationEventsProps> = ({
|
||||
severity="warning"
|
||||
prominent
|
||||
actions={
|
||||
<Button variant="subtle" size="sm" asChild>
|
||||
<Button size="sm" asChild>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useTheme } from "@emotion/react";
|
||||
import Link from "@mui/material/Link";
|
||||
import {
|
||||
CircleAlertIcon,
|
||||
CircleCheckIcon,
|
||||
@@ -14,6 +13,7 @@ import {
|
||||
type ReactElement,
|
||||
} from "react";
|
||||
import type { HealthCode, HealthSeverity } from "#/api/typesGenerated";
|
||||
import { Link } from "#/components/Link/Link";
|
||||
import { docs } from "#/utils/docs";
|
||||
import { healthyColor } from "./healthyColor";
|
||||
|
||||
@@ -242,6 +242,7 @@ export const HealthMessageDocsLink: FC<HealthMessageDocsLinkProps> = ({
|
||||
href={docs(`/admin/monitoring/health-check#${code.toLocaleLowerCase()}`)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="mx-0"
|
||||
>
|
||||
Docs for {code}
|
||||
</Link>
|
||||
|
||||
@@ -23,7 +23,7 @@ import type {
|
||||
VariableValue,
|
||||
WorkspaceResource,
|
||||
} from "#/api/typesGenerated";
|
||||
import { Alert } from "#/components/Alert/Alert";
|
||||
import { Alert, AlertTitle } from "#/components/Alert/Alert";
|
||||
import { Button } from "#/components/Button/Button";
|
||||
import { Sidebar } from "#/components/FullPageLayout/Sidebar";
|
||||
import {
|
||||
@@ -306,16 +306,14 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
|
||||
prominent
|
||||
dismissible
|
||||
actions={
|
||||
<Button
|
||||
variant="subtle"
|
||||
size="sm"
|
||||
onClick={onCreateWorkspace}
|
||||
>
|
||||
<Button size="sm" onClick={onCreateWorkspace}>
|
||||
Create a workspace
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
Successfully published {publishedVersion.name}!
|
||||
<AlertTitle>
|
||||
Successfully published {publishedVersion.name}!
|
||||
</AlertTitle>
|
||||
</Alert>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -10,7 +10,7 @@ export const WorkspaceDeletedBanner: FC<WorkspaceDeletedBannerProps> = ({
|
||||
handleClick,
|
||||
}) => {
|
||||
const NewWorkspaceButton = (
|
||||
<Button onClick={handleClick} size="sm" variant="subtle">
|
||||
<Button onClick={handleClick} size="sm">
|
||||
Create new workspace
|
||||
</Button>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user