mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
refactor(site): make HelpTooltip easier to reuse and compose (#11242)
This commit is contained in:
@@ -17,6 +17,8 @@ import {
|
||||
HelpTooltip,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipText,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import dayjs from "dayjs";
|
||||
import { useTheme } from "@emotion/react";
|
||||
@@ -139,12 +141,15 @@ export const ActiveUsersTitle: FC = () => {
|
||||
return (
|
||||
<div css={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||
Active Users
|
||||
<HelpTooltip size="small">
|
||||
<HelpTooltipTitle>How do we calculate active users?</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
When a connection is initiated to a user's workspace they are
|
||||
considered an active user. e.g. apps, web terminal, SSH
|
||||
</HelpTooltipText>
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger size="small" />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>How do we calculate active users?</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
When a connection is initiated to a user's workspace they are
|
||||
considered an active user. e.g. apps, web terminal, SSH
|
||||
</HelpTooltipText>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,170 +1,107 @@
|
||||
import Link from "@mui/material/Link";
|
||||
// This is used as base for the main HelpTooltip component
|
||||
// eslint-disable-next-line no-restricted-imports -- Read above
|
||||
import Popover, { type PopoverProps } from "@mui/material/Popover";
|
||||
import HelpIcon from "@mui/icons-material/HelpOutline";
|
||||
import OpenInNewIcon from "@mui/icons-material/OpenInNew";
|
||||
import {
|
||||
createContext,
|
||||
useContext,
|
||||
useRef,
|
||||
useState,
|
||||
type FC,
|
||||
type PropsWithChildren,
|
||||
type HTMLAttributes,
|
||||
type ReactNode,
|
||||
forwardRef,
|
||||
ComponentProps,
|
||||
} from "react";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import type { CSSObject } from "@emotion/css";
|
||||
import { type CSSObject } from "@emotion/css";
|
||||
import { css, type Interpolation, type Theme, useTheme } from "@emotion/react";
|
||||
import { type ClassName, useClassName } from "hooks/useClassName";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
usePopover,
|
||||
} from "components/Popover/Popover";
|
||||
|
||||
type Icon = typeof HelpIcon;
|
||||
|
||||
type Size = "small" | "medium";
|
||||
|
||||
export const HelpTooltipContext = createContext<
|
||||
{ open: boolean; onClose: () => void } | undefined
|
||||
>(undefined);
|
||||
export const HelpTooltipIcon = HelpIcon;
|
||||
|
||||
const useHelpTooltip = () => {
|
||||
const helpTooltipContext = useContext(HelpTooltipContext);
|
||||
|
||||
if (!helpTooltipContext) {
|
||||
throw new Error(
|
||||
"This hook should be used in side of the HelpTooltipContext.",
|
||||
);
|
||||
}
|
||||
|
||||
return helpTooltipContext;
|
||||
export const HelpTooltip: FC<ComponentProps<typeof Popover>> = (props) => {
|
||||
return <Popover mode="hover" {...props} />;
|
||||
};
|
||||
|
||||
interface HelpPopoverProps extends PopoverProps {
|
||||
onOpen: () => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const HelpPopover: FC<HelpPopoverProps> = ({
|
||||
onOpen,
|
||||
onClose,
|
||||
children,
|
||||
...props
|
||||
}) => {
|
||||
const popover = useClassName(classNames.popover, []);
|
||||
const paper = useClassName(classNames.paper, []);
|
||||
export const HelpTooltipContent = (
|
||||
props: ComponentProps<typeof PopoverContent>,
|
||||
) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Popover
|
||||
className={popover}
|
||||
classes={{ paper }}
|
||||
onClose={onClose}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "left",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "left",
|
||||
}}
|
||||
PaperProps={{
|
||||
onMouseEnter: onOpen,
|
||||
onMouseLeave: onClose,
|
||||
}}
|
||||
<PopoverContent
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</Popover>
|
||||
css={{
|
||||
"& .MuiPaper-root": {
|
||||
fontSize: 14,
|
||||
width: 304,
|
||||
padding: 20,
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export interface HelpTooltipProps {
|
||||
// Useful to test on storybook
|
||||
open?: boolean;
|
||||
type HelpTooltipTriggerProps = HTMLAttributes<HTMLButtonElement> & {
|
||||
size?: Size;
|
||||
icon?: Icon;
|
||||
buttonStyles?: Interpolation<Theme>;
|
||||
iconStyles?: Interpolation<Theme>;
|
||||
children?: ReactNode;
|
||||
}
|
||||
hoverEffect?: boolean;
|
||||
};
|
||||
|
||||
export const HelpTooltip: FC<HelpTooltipProps> = ({
|
||||
children,
|
||||
open = false,
|
||||
size = "medium",
|
||||
icon: Icon = HelpIcon,
|
||||
buttonStyles,
|
||||
iconStyles,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const anchorRef = useRef<HTMLButtonElement>(null);
|
||||
const [isOpen, setIsOpen] = useState(open);
|
||||
const id = isOpen ? "help-popover" : undefined;
|
||||
export const HelpTooltipTrigger = forwardRef<
|
||||
HTMLButtonElement,
|
||||
HelpTooltipTriggerProps
|
||||
>((props, ref) => {
|
||||
const {
|
||||
size = "medium",
|
||||
children = <HelpTooltipIcon />,
|
||||
hoverEffect = true,
|
||||
...buttonProps
|
||||
} = props;
|
||||
|
||||
const onClose = () => {
|
||||
setIsOpen(false);
|
||||
};
|
||||
const hoverEffectStyles = css({
|
||||
opacity: 0.5,
|
||||
"&:hover": {
|
||||
opacity: 0.75,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<PopoverTrigger>
|
||||
<button
|
||||
ref={anchorRef}
|
||||
aria-describedby={id}
|
||||
{...buttonProps}
|
||||
aria-label="More info"
|
||||
ref={ref}
|
||||
css={[
|
||||
css`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: ${theme.spacing(getButtonSpacingFromSize(size))};
|
||||
height: ${theme.spacing(getButtonSpacingFromSize(size))};
|
||||
padding: 0;
|
||||
padding: 4px 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: ${theme.palette.text.primary};
|
||||
opacity: 0.5;
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.75;
|
||||
& svg {
|
||||
width: ${getIconSpacingFromSize(size)}px;
|
||||
height: ${getIconSpacingFromSize(size)}px;
|
||||
}
|
||||
`,
|
||||
buttonStyles,
|
||||
hoverEffect ? hoverEffectStyles : null,
|
||||
]}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
setIsOpen(true);
|
||||
}}
|
||||
onMouseEnter={() => {
|
||||
setIsOpen(true);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setIsOpen(false);
|
||||
}}
|
||||
aria-label="More info"
|
||||
>
|
||||
<Icon
|
||||
css={[
|
||||
{
|
||||
width: theme.spacing(getIconSpacingFromSize(size)),
|
||||
height: theme.spacing(getIconSpacingFromSize(size)),
|
||||
},
|
||||
iconStyles,
|
||||
]}
|
||||
/>
|
||||
{children}
|
||||
</button>
|
||||
<HelpPopover
|
||||
id={id}
|
||||
open={isOpen}
|
||||
anchorEl={anchorRef.current}
|
||||
onOpen={() => setIsOpen(true)}
|
||||
onClose={() => setIsOpen(false)}
|
||||
>
|
||||
<HelpTooltipContext.Provider value={{ open: isOpen, onClose }}>
|
||||
{children}
|
||||
</HelpTooltipContext.Provider>
|
||||
</HelpPopover>
|
||||
</>
|
||||
</PopoverTrigger>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export const HelpTooltipTitle: FC<HTMLAttributes<HTMLHeadingElement>> = ({
|
||||
children,
|
||||
@@ -213,7 +150,7 @@ export const HelpTooltipAction: FC<HelpTooltipActionProps> = ({
|
||||
onClick,
|
||||
ariaLabel,
|
||||
}) => {
|
||||
const tooltip = useHelpTooltip();
|
||||
const popover = usePopover();
|
||||
|
||||
return (
|
||||
<button
|
||||
@@ -222,7 +159,7 @@ export const HelpTooltipAction: FC<HelpTooltipActionProps> = ({
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onClick();
|
||||
tooltip.onClose();
|
||||
popover.setIsOpen(false);
|
||||
}}
|
||||
>
|
||||
<Icon css={styles.actionIcon} />
|
||||
@@ -239,42 +176,16 @@ export const HelpTooltipLinksGroup: FC<PropsWithChildren> = ({ children }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const getButtonSpacingFromSize = (size?: Size): number => {
|
||||
switch (size) {
|
||||
case "small":
|
||||
return 2.5;
|
||||
case "medium":
|
||||
default:
|
||||
return 3;
|
||||
}
|
||||
};
|
||||
|
||||
const getIconSpacingFromSize = (size?: Size): number => {
|
||||
switch (size) {
|
||||
case "small":
|
||||
return 1.5;
|
||||
return 12;
|
||||
case "medium":
|
||||
default:
|
||||
return 2;
|
||||
return 16;
|
||||
}
|
||||
};
|
||||
|
||||
const classNames = {
|
||||
popover: (css) => css`
|
||||
pointer-events: none;
|
||||
`,
|
||||
|
||||
paper: (css, theme) => css`
|
||||
${theme.typography.body2 as CSSObject}
|
||||
|
||||
margin-top: 4px;
|
||||
width: 304px;
|
||||
padding: 20px;
|
||||
color: ${theme.palette.text.secondary};
|
||||
pointer-events: auto;
|
||||
`,
|
||||
} satisfies Record<string, ClassName>;
|
||||
|
||||
const styles = {
|
||||
title: (theme) => ({
|
||||
marginTop: 0,
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { type FC, type ReactNode } from "react";
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipIcon,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import InfoIcon from "@mui/icons-material/InfoOutlined";
|
||||
import { Interpolation, Theme, css, useTheme } from "@emotion/react";
|
||||
|
||||
interface InfoTooltipProps {
|
||||
@@ -22,14 +24,16 @@ export const InfoTooltip: FC<InfoTooltipProps> = ({
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<HelpTooltip
|
||||
size="small"
|
||||
icon={InfoIcon}
|
||||
iconStyles={{ color: theme.experimental.roles[type].outline }}
|
||||
buttonStyles={styles.button}
|
||||
>
|
||||
<HelpTooltipTitle>{title}</HelpTooltipTitle>
|
||||
<HelpTooltipText>{message}</HelpTooltipText>
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger size="small" css={styles.button}>
|
||||
<HelpTooltipIcon
|
||||
css={{ color: theme.experimental.roles[type].outline }}
|
||||
/>
|
||||
</HelpTooltipTrigger>
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>{title}</HelpTooltipTitle>
|
||||
<HelpTooltipText>{message}</HelpTooltipText>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
useId,
|
||||
useRef,
|
||||
useState,
|
||||
HTMLAttributes,
|
||||
} from "react";
|
||||
// This is used as base for the main Popover component
|
||||
// eslint-disable-next-line no-restricted-imports -- Read above
|
||||
@@ -79,8 +80,11 @@ export const usePopover = () => {
|
||||
return context;
|
||||
};
|
||||
|
||||
export const PopoverTrigger = (props: { children: TriggerElement }) => {
|
||||
export const PopoverTrigger = (
|
||||
props: HTMLAttributes<HTMLElement> & { children: TriggerElement },
|
||||
) => {
|
||||
const popover = usePopover();
|
||||
const { children, ...elementProps } = props;
|
||||
|
||||
const clickProps = {
|
||||
onClick: () => {
|
||||
@@ -98,6 +102,7 @@ export const PopoverTrigger = (props: { children: TriggerElement }) => {
|
||||
};
|
||||
|
||||
return cloneElement(props.children, {
|
||||
...elementProps,
|
||||
...(popover.mode === "click" ? clickProps : hoverProps),
|
||||
"aria-haspopup": true,
|
||||
"aria-owns": popover.isOpen ? popover.id : undefined,
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { type FC, useRef, useState } from "react";
|
||||
import { type FC } from "react";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import type { WorkspaceAgent, DERPRegion } from "api/typesGenerated";
|
||||
import {
|
||||
HelpTooltipText,
|
||||
HelpPopover,
|
||||
HelpTooltip,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipContent,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { getLatencyColor } from "utils/latency";
|
||||
import { PopoverTrigger } from "components/Popover/Popover";
|
||||
|
||||
const getDisplayLatency = (theme: Theme, agent: WorkspaceAgent) => {
|
||||
// Find the right latency to display
|
||||
@@ -31,9 +33,6 @@ const getDisplayLatency = (theme: Theme, agent: WorkspaceAgent) => {
|
||||
|
||||
export const AgentLatency: FC<{ agent: WorkspaceAgent }> = ({ agent }) => {
|
||||
const theme = useTheme();
|
||||
const anchorRef = useRef<HTMLButtonElement>(null);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const id = isOpen ? "latency-popover" : undefined;
|
||||
const latency = getDisplayLatency(theme, agent);
|
||||
|
||||
if (!latency || !agent.latency) {
|
||||
@@ -41,24 +40,17 @@ export const AgentLatency: FC<{ agent: WorkspaceAgent }> = ({ agent }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<span
|
||||
role="presentation"
|
||||
aria-label="latency"
|
||||
ref={anchorRef}
|
||||
onMouseEnter={() => setIsOpen(true)}
|
||||
onMouseLeave={() => setIsOpen(false)}
|
||||
css={{ cursor: "pointer", color: latency.color }}
|
||||
>
|
||||
{Math.round(Math.round(latency.latency_ms))}ms
|
||||
</span>
|
||||
<HelpPopover
|
||||
id={id}
|
||||
open={isOpen}
|
||||
anchorEl={anchorRef.current}
|
||||
onOpen={() => setIsOpen(true)}
|
||||
onClose={() => setIsOpen(false)}
|
||||
>
|
||||
<HelpTooltip>
|
||||
<PopoverTrigger>
|
||||
<span
|
||||
role="presentation"
|
||||
aria-label="latency"
|
||||
css={{ cursor: "pointer", color: latency.color }}
|
||||
>
|
||||
{Math.round(latency.latency_ms)}ms
|
||||
</span>
|
||||
</PopoverTrigger>
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>Latency</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
This is the latency overhead on non peer to peer connections. The
|
||||
@@ -86,7 +78,7 @@ export const AgentLatency: FC<{ agent: WorkspaceAgent }> = ({ agent }) => {
|
||||
))}
|
||||
</Stack>
|
||||
</HelpTooltipText>
|
||||
</HelpPopover>
|
||||
</>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { type ComponentProps, type FC } from "react";
|
||||
import { type FC } from "react";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import RefreshIcon from "@mui/icons-material/RefreshOutlined";
|
||||
import {
|
||||
HelpPopover,
|
||||
HelpTooltip,
|
||||
HelpTooltipAction,
|
||||
HelpTooltipContext,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipLinksGroup,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
@@ -12,8 +12,9 @@ import {
|
||||
import type { WorkspaceAgent } from "api/typesGenerated";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { agentVersionStatus } from "../../utils/workspace";
|
||||
import { PopoverTrigger } from "components/Popover/Popover";
|
||||
|
||||
type AgentOutdatedTooltipProps = ComponentProps<typeof HelpPopover> & {
|
||||
type AgentOutdatedTooltipProps = {
|
||||
agent: WorkspaceAgent;
|
||||
serverVersion: string;
|
||||
status: agentVersionStatus;
|
||||
@@ -25,11 +26,6 @@ export const AgentOutdatedTooltip: FC<AgentOutdatedTooltipProps> = ({
|
||||
serverVersion,
|
||||
status,
|
||||
onUpdate,
|
||||
onOpen,
|
||||
id,
|
||||
open,
|
||||
onClose,
|
||||
anchorEl,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const versionLabelStyles = {
|
||||
@@ -50,14 +46,13 @@ export const AgentOutdatedTooltip: FC<AgentOutdatedTooltipProps> = ({
|
||||
"To fix this, you can stop and start the workspace.";
|
||||
|
||||
return (
|
||||
<HelpPopover
|
||||
id={id}
|
||||
open={open}
|
||||
anchorEl={anchorEl}
|
||||
onOpen={onOpen}
|
||||
onClose={onClose}
|
||||
>
|
||||
<HelpTooltipContext.Provider value={{ open, onClose }}>
|
||||
<HelpTooltip>
|
||||
<PopoverTrigger>
|
||||
<span role="status" css={{ cursor: "pointer" }}>
|
||||
{status === agentVersionStatus.Outdated ? "Outdated" : "Deprecated"}
|
||||
</span>
|
||||
</PopoverTrigger>
|
||||
<HelpTooltipContent>
|
||||
<Stack spacing={1}>
|
||||
<div>
|
||||
<HelpTooltipTitle>{title}</HelpTooltipTitle>
|
||||
@@ -84,7 +79,7 @@ export const AgentOutdatedTooltip: FC<AgentOutdatedTooltipProps> = ({
|
||||
</HelpTooltipAction>
|
||||
</HelpTooltipLinksGroup>
|
||||
</Stack>
|
||||
</HelpTooltipContext.Provider>
|
||||
</HelpPopover>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,12 +4,13 @@ import { WorkspaceAgent } from "api/typesGenerated";
|
||||
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne";
|
||||
import WarningRounded from "@mui/icons-material/WarningRounded";
|
||||
import {
|
||||
HelpPopover,
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { useRef, useState } from "react";
|
||||
import Link from "@mui/material/Link";
|
||||
import { PopoverTrigger } from "components/Popover/Popover";
|
||||
|
||||
// If we think in the agent status and lifecycle into a single enum/state I’d
|
||||
// say we would have: connecting, timeout, disconnected, connected:created,
|
||||
@@ -43,27 +44,13 @@ const StartingLifecycle: React.FC = () => {
|
||||
const StartTimeoutLifecycle: React.FC<{
|
||||
agent: WorkspaceAgent;
|
||||
}> = ({ agent }) => {
|
||||
const anchorRef = useRef<SVGSVGElement>(null);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const id = isOpen ? "timeout-popover" : undefined;
|
||||
|
||||
return (
|
||||
<>
|
||||
<WarningRounded
|
||||
ref={anchorRef}
|
||||
onMouseEnter={() => setIsOpen(true)}
|
||||
onMouseLeave={() => setIsOpen(false)}
|
||||
role="status"
|
||||
aria-label="Start timeout"
|
||||
css={styles.timeoutWarning}
|
||||
/>
|
||||
<HelpPopover
|
||||
id={id}
|
||||
open={isOpen}
|
||||
anchorEl={anchorRef.current}
|
||||
onOpen={() => setIsOpen(true)}
|
||||
onClose={() => setIsOpen(false)}
|
||||
>
|
||||
<HelpTooltip>
|
||||
<PopoverTrigger role="status" aria-label="Agent timeout">
|
||||
<WarningRounded css={styles.timeoutWarning} />
|
||||
</PopoverTrigger>
|
||||
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>Agent is taking too long to start</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
We noticed this agent is taking longer than expected to start.{" "}
|
||||
@@ -76,35 +63,20 @@ const StartTimeoutLifecycle: React.FC<{
|
||||
</Link>
|
||||
.
|
||||
</HelpTooltipText>
|
||||
</HelpPopover>
|
||||
</>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const StartErrorLifecycle: React.FC<{
|
||||
agent: WorkspaceAgent;
|
||||
}> = ({ agent }) => {
|
||||
const anchorRef = useRef<SVGSVGElement>(null);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const id = isOpen ? "timeout-popover" : undefined;
|
||||
|
||||
return (
|
||||
<>
|
||||
<WarningRounded
|
||||
ref={anchorRef}
|
||||
onMouseEnter={() => setIsOpen(true)}
|
||||
onMouseLeave={() => setIsOpen(false)}
|
||||
role="status"
|
||||
aria-label="Start error"
|
||||
css={styles.errorWarning}
|
||||
/>
|
||||
<HelpPopover
|
||||
id={id}
|
||||
open={isOpen}
|
||||
anchorEl={anchorRef.current}
|
||||
onOpen={() => setIsOpen(true)}
|
||||
onClose={() => setIsOpen(false)}
|
||||
>
|
||||
<HelpTooltip>
|
||||
<PopoverTrigger role="status" aria-label="Start error">
|
||||
<WarningRounded css={styles.errorWarning} />
|
||||
</PopoverTrigger>
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>Error starting the agent</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
Something went wrong during the agent startup.{" "}
|
||||
@@ -117,8 +89,8 @@ const StartErrorLifecycle: React.FC<{
|
||||
</Link>
|
||||
.
|
||||
</HelpTooltipText>
|
||||
</HelpPopover>
|
||||
</>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -137,27 +109,12 @@ const ShuttingDownLifecycle: React.FC = () => {
|
||||
const ShutdownTimeoutLifecycle: React.FC<{
|
||||
agent: WorkspaceAgent;
|
||||
}> = ({ agent }) => {
|
||||
const anchorRef = useRef<SVGSVGElement>(null);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const id = isOpen ? "timeout-popover" : undefined;
|
||||
|
||||
return (
|
||||
<>
|
||||
<WarningRounded
|
||||
ref={anchorRef}
|
||||
onMouseEnter={() => setIsOpen(true)}
|
||||
onMouseLeave={() => setIsOpen(false)}
|
||||
role="status"
|
||||
aria-label="Stop timeout"
|
||||
css={styles.timeoutWarning}
|
||||
/>
|
||||
<HelpPopover
|
||||
id={id}
|
||||
open={isOpen}
|
||||
anchorEl={anchorRef.current}
|
||||
onOpen={() => setIsOpen(true)}
|
||||
onClose={() => setIsOpen(false)}
|
||||
>
|
||||
<HelpTooltip>
|
||||
<PopoverTrigger role="status" aria-label="Stop timeout">
|
||||
<WarningRounded css={styles.timeoutWarning} />
|
||||
</PopoverTrigger>
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>Agent is taking too long to stop</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
We noticed this agent is taking longer than expected to stop.{" "}
|
||||
@@ -170,35 +127,20 @@ const ShutdownTimeoutLifecycle: React.FC<{
|
||||
</Link>
|
||||
.
|
||||
</HelpTooltipText>
|
||||
</HelpPopover>
|
||||
</>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const ShutdownErrorLifecycle: React.FC<{
|
||||
agent: WorkspaceAgent;
|
||||
}> = ({ agent }) => {
|
||||
const anchorRef = useRef<SVGSVGElement>(null);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const id = isOpen ? "timeout-popover" : undefined;
|
||||
|
||||
return (
|
||||
<>
|
||||
<WarningRounded
|
||||
ref={anchorRef}
|
||||
onMouseEnter={() => setIsOpen(true)}
|
||||
onMouseLeave={() => setIsOpen(false)}
|
||||
role="status"
|
||||
aria-label="Stop error"
|
||||
css={styles.errorWarning}
|
||||
/>
|
||||
<HelpPopover
|
||||
id={id}
|
||||
open={isOpen}
|
||||
anchorEl={anchorRef.current}
|
||||
onOpen={() => setIsOpen(true)}
|
||||
onClose={() => setIsOpen(false)}
|
||||
>
|
||||
<HelpTooltip>
|
||||
<PopoverTrigger role="status" aria-label="Stop error">
|
||||
<WarningRounded css={styles.errorWarning} />
|
||||
</PopoverTrigger>
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>Error stopping the agent</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
Something went wrong while trying to stop the agent.{" "}
|
||||
@@ -211,8 +153,8 @@ const ShutdownErrorLifecycle: React.FC<{
|
||||
</Link>
|
||||
.
|
||||
</HelpTooltipText>
|
||||
</HelpPopover>
|
||||
</>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -293,27 +235,12 @@ const ConnectingStatus: React.FC = () => {
|
||||
const TimeoutStatus: React.FC<{
|
||||
agent: WorkspaceAgent;
|
||||
}> = ({ agent }) => {
|
||||
const anchorRef = useRef<SVGSVGElement>(null);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const id = isOpen ? "timeout-popover" : undefined;
|
||||
|
||||
return (
|
||||
<>
|
||||
<WarningRounded
|
||||
ref={anchorRef}
|
||||
onMouseEnter={() => setIsOpen(true)}
|
||||
onMouseLeave={() => setIsOpen(false)}
|
||||
role="status"
|
||||
aria-label="Timeout"
|
||||
css={styles.timeoutWarning}
|
||||
/>
|
||||
<HelpPopover
|
||||
id={id}
|
||||
open={isOpen}
|
||||
anchorEl={anchorRef.current}
|
||||
onOpen={() => setIsOpen(true)}
|
||||
onClose={() => setIsOpen(false)}
|
||||
>
|
||||
<HelpTooltip>
|
||||
<PopoverTrigger role="status" aria-label="Timeout">
|
||||
<WarningRounded css={styles.timeoutWarning} />
|
||||
</PopoverTrigger>
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>Agent is taking too long to connect</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
We noticed this agent is taking longer than expected to connect.{" "}
|
||||
@@ -326,8 +253,8 @@ const TimeoutStatus: React.FC<{
|
||||
</Link>
|
||||
.
|
||||
</HelpTooltipText>
|
||||
</HelpPopover>
|
||||
</>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type FC, useRef, useState } from "react";
|
||||
import { type FC } from "react";
|
||||
import type { WorkspaceAgent } from "api/typesGenerated";
|
||||
import { agentVersionStatus, getDisplayVersionStatus } from "utils/workspace";
|
||||
import { AgentOutdatedTooltip } from "./AgentOutdatedTooltip";
|
||||
@@ -9,9 +9,6 @@ export const AgentVersion: FC<{
|
||||
serverAPIVersion: string;
|
||||
onUpdate: () => void;
|
||||
}> = ({ agent, serverVersion, serverAPIVersion, onUpdate }) => {
|
||||
const anchorRef = useRef<HTMLButtonElement>(null);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const id = isOpen ? "version-outdated-popover" : undefined;
|
||||
const { status } = getDisplayVersionStatus(
|
||||
agent.version,
|
||||
serverVersion,
|
||||
@@ -24,28 +21,11 @@ export const AgentVersion: FC<{
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<span
|
||||
role="presentation"
|
||||
aria-label="latency"
|
||||
ref={anchorRef}
|
||||
onMouseEnter={() => setIsOpen(true)}
|
||||
onMouseLeave={() => setIsOpen(false)}
|
||||
css={{ cursor: "pointer" }}
|
||||
>
|
||||
{status === agentVersionStatus.Outdated ? "Outdated" : "Deprecated"}
|
||||
</span>
|
||||
<AgentOutdatedTooltip
|
||||
id={id}
|
||||
open={isOpen}
|
||||
anchorEl={anchorRef.current}
|
||||
onOpen={() => setIsOpen(true)}
|
||||
onClose={() => setIsOpen(false)}
|
||||
agent={agent}
|
||||
serverVersion={serverVersion}
|
||||
status={status}
|
||||
onUpdate={onUpdate}
|
||||
/>
|
||||
</>
|
||||
<AgentOutdatedTooltip
|
||||
agent={agent}
|
||||
serverVersion={serverVersion}
|
||||
status={status}
|
||||
onUpdate={onUpdate}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,15 +4,18 @@ import Skeleton from "@mui/material/Skeleton";
|
||||
import Link from "@mui/material/Link";
|
||||
import { type FC } from "react";
|
||||
import { useQuery } from "react-query";
|
||||
import { type Interpolation, type Theme, css, useTheme } from "@emotion/react";
|
||||
import { type Interpolation, type Theme, useTheme } from "@emotion/react";
|
||||
import { templateVersion } from "api/queries/templates";
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipAction,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipLinksGroup,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { usePopover } from "components/Popover/Popover";
|
||||
|
||||
export const Language = {
|
||||
outdatedLabel: "Outdated",
|
||||
@@ -28,22 +31,33 @@ interface TooltipProps {
|
||||
ariaLabel?: string;
|
||||
}
|
||||
|
||||
export const WorkspaceOutdatedTooltip: FC<TooltipProps> = ({
|
||||
onUpdateVersion,
|
||||
ariaLabel,
|
||||
latestVersionId,
|
||||
templateName,
|
||||
}) => {
|
||||
const { data: activeVersion } = useQuery(templateVersion(latestVersionId));
|
||||
export const WorkspaceOutdatedTooltip: FC<TooltipProps> = (props) => {
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger
|
||||
size="small"
|
||||
aria-label="More info"
|
||||
hoverEffect={false}
|
||||
>
|
||||
<InfoIcon css={styles.icon} />
|
||||
</HelpTooltipTrigger>
|
||||
|
||||
<OutdatedTooltipContent {...props} />
|
||||
</HelpTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const OutdatedTooltipContent = (props: TooltipProps) => {
|
||||
const popover = usePopover();
|
||||
const { onUpdateVersion, ariaLabel, latestVersionId, templateName } = props;
|
||||
const { data: activeVersion } = useQuery({
|
||||
...templateVersion(latestVersionId),
|
||||
enabled: popover.isOpen,
|
||||
});
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<HelpTooltip
|
||||
size="small"
|
||||
icon={InfoIcon}
|
||||
iconStyles={styles.icon}
|
||||
buttonStyles={styles.button}
|
||||
>
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>{Language.outdatedLabel}</HelpTooltipTitle>
|
||||
<HelpTooltipText>{Language.versionTooltipText}</HelpTooltipText>
|
||||
|
||||
@@ -90,7 +104,7 @@ export const WorkspaceOutdatedTooltip: FC<TooltipProps> = ({
|
||||
{Language.updateVersionLabel}
|
||||
</HelpTooltipAction>
|
||||
</HelpTooltipLinksGroup>
|
||||
</HelpTooltip>
|
||||
</HelpTooltipContent>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -99,14 +113,6 @@ const styles = {
|
||||
color: theme.experimental.roles.notice.outline,
|
||||
}),
|
||||
|
||||
button: css`
|
||||
opacity: 1;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
`,
|
||||
|
||||
container: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { FC } from "react";
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipLink,
|
||||
HelpTooltipLinksGroup,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { docs } from "utils/docs";
|
||||
|
||||
@@ -17,13 +19,17 @@ export const Language = {
|
||||
export const AuditHelpTooltip: FC = () => {
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTitle>{Language.title}</HelpTooltipTitle>
|
||||
<HelpTooltipText>{Language.body}</HelpTooltipText>
|
||||
<HelpTooltipLinksGroup>
|
||||
<HelpTooltipLink href={docs("/admin/audit-logs")}>
|
||||
{Language.docs}
|
||||
</HelpTooltipLink>
|
||||
</HelpTooltipLinksGroup>
|
||||
<HelpTooltipTrigger />
|
||||
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>{Language.title}</HelpTooltipTitle>
|
||||
<HelpTooltipText>{Language.body}</HelpTooltipText>
|
||||
<HelpTooltipLinksGroup>
|
||||
<HelpTooltipLink href={docs("/admin/audit-logs")}>
|
||||
{Language.docs}
|
||||
</HelpTooltipLink>
|
||||
</HelpTooltipLinksGroup>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -23,7 +23,9 @@ import * as Yup from "yup";
|
||||
import { WorkspaceBuildLogs } from "components/WorkspaceBuildLogs/WorkspaceBuildLogs";
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { LazyIconField } from "components/IconField/LazyIconField";
|
||||
import Link from "@mui/material/Link";
|
||||
@@ -572,10 +574,13 @@ export const CreateTemplateForm: FC<CreateTemplateFormProps> = (props) => {
|
||||
</strong>
|
||||
|
||||
<HelpTooltip>
|
||||
<HelpTooltipText>
|
||||
If checked, users may be able to corrupt their
|
||||
workspace.
|
||||
</HelpTooltipText>
|
||||
<HelpTooltipTrigger size="small" />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipText>
|
||||
If checked, users may be able to corrupt their
|
||||
workspace.
|
||||
</HelpTooltipText>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
</Stack>
|
||||
<span css={styles.optionHelperText}>
|
||||
@@ -608,16 +613,21 @@ export const CreateTemplateForm: FC<CreateTemplateFormProps> = (props) => {
|
||||
<strong>Allow everyone to use the template</strong>
|
||||
|
||||
<HelpTooltip>
|
||||
<HelpTooltipText>
|
||||
If unchecked, only users with the 'template
|
||||
admin' and 'owner' role can use this
|
||||
template until the permissions are updated. Navigate to{" "}
|
||||
<strong>
|
||||
Templates > Select a template > Settings >
|
||||
Permissions
|
||||
</strong>{" "}
|
||||
to update permissions.
|
||||
</HelpTooltipText>
|
||||
<HelpTooltipTrigger size="small" />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipText>
|
||||
If unchecked, only users with the 'template
|
||||
admin' and 'owner' role can use this
|
||||
template until the permissions are updated. Navigate
|
||||
to{" "}
|
||||
<strong>
|
||||
Templates <MenuPath /> Select a template{" "}
|
||||
<MenuPath /> Settings <MenuPath />
|
||||
Permissions
|
||||
</strong>{" "}
|
||||
to update permissions.
|
||||
</HelpTooltipText>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
</Stack>
|
||||
<span css={styles.optionHelperText}>
|
||||
@@ -684,6 +694,10 @@ export const CreateTemplateForm: FC<CreateTemplateFormProps> = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
const MenuPath = () => {
|
||||
return <span aria-label="in">></span>;
|
||||
};
|
||||
|
||||
const fillNameAndDisplayWithFilename = async (
|
||||
filename: string,
|
||||
form: ReturnType<typeof useFormik<CreateTemplateData>>,
|
||||
|
||||
@@ -46,6 +46,8 @@ import {
|
||||
HelpTooltip,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipText,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { UserAvatar } from "components/UserAvatar/UserAvatar";
|
||||
import { getLatencyColor } from "utils/latency";
|
||||
@@ -281,14 +283,18 @@ const UsersLatencyPanel: FC<UsersLatencyPanelProps> = ({
|
||||
<PanelHeader>
|
||||
<PanelTitle css={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||
Latency by user
|
||||
<HelpTooltip size="small">
|
||||
<HelpTooltipTitle>How is latency calculated?</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
The median round trip time of user connections to workspaces.
|
||||
</HelpTooltipText>
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger size="small" />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>How is latency calculated?</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
The median round trip time of user connections to workspaces.
|
||||
</HelpTooltipText>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
</PanelTitle>
|
||||
</PanelHeader>
|
||||
|
||||
<PanelContent>
|
||||
{!data && <Loader css={{ height: "100%" }} />}
|
||||
{users && users.length === 0 && <NoDataAvailable />}
|
||||
@@ -348,12 +354,15 @@ const UsersActivityPanel: FC<UsersActivityPanelProps> = ({
|
||||
<PanelHeader>
|
||||
<PanelTitle css={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||
Activity by user
|
||||
<HelpTooltip size="small">
|
||||
<HelpTooltipTitle>How is activity calculated?</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
When a connection is initiated to a user's workspace they are
|
||||
considered an active user. e.g. apps, web terminal, SSH
|
||||
</HelpTooltipText>
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger size="small" />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>How is activity calculated?</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
When a connection is initiated to a user's workspace they
|
||||
are considered an active user. e.g. apps, web terminal, SSH
|
||||
</HelpTooltipText>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
</PanelTitle>
|
||||
</PanelHeader>
|
||||
|
||||
+15
-6
@@ -22,7 +22,9 @@ import { Stack } from "components/Stack/Stack";
|
||||
import Checkbox from "@mui/material/Checkbox";
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { EnterpriseBadge } from "components/Badges/Badges";
|
||||
|
||||
@@ -161,9 +163,13 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
|
||||
>
|
||||
Allow users to cancel in-progress workspace jobs.
|
||||
<HelpTooltip>
|
||||
<HelpTooltipText>
|
||||
If checked, users may be able to corrupt their workspace.
|
||||
</HelpTooltipText>
|
||||
<HelpTooltipTrigger />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipText>
|
||||
If checked, users may be able to corrupt their
|
||||
workspace.
|
||||
</HelpTooltipText>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
</Stack>
|
||||
<span css={styles.optionHelperText}>
|
||||
@@ -193,9 +199,12 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
|
||||
>
|
||||
Require workspaces automatically update when started.
|
||||
<HelpTooltip>
|
||||
<HelpTooltipText>
|
||||
This setting is not enforced for template admins.
|
||||
</HelpTooltipText>
|
||||
<HelpTooltipTrigger />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipText>
|
||||
This setting is not enforced for template admins.
|
||||
</HelpTooltipText>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
</Stack>
|
||||
<span css={styles.optionHelperText}>
|
||||
|
||||
@@ -28,10 +28,12 @@ import {
|
||||
} from "components/TableLoader/TableLoader";
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipLink,
|
||||
HelpTooltipLinksGroup,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { EmptyTemplates } from "./EmptyTemplates";
|
||||
import { useClickableTableRow } from "hooks/useClickableTableRow";
|
||||
@@ -63,13 +65,16 @@ export const Language = {
|
||||
const TemplateHelpTooltip: FC = () => {
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTitle>{Language.templateTooltipTitle}</HelpTooltipTitle>
|
||||
<HelpTooltipText>{Language.templateTooltipText}</HelpTooltipText>
|
||||
<HelpTooltipLinksGroup>
|
||||
<HelpTooltipLink href={docs("/templates")}>
|
||||
{Language.templateTooltipLink}
|
||||
</HelpTooltipLink>
|
||||
</HelpTooltipLinksGroup>
|
||||
<HelpTooltipTrigger />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>{Language.templateTooltipTitle}</HelpTooltipTitle>
|
||||
<HelpTooltipText>{Language.templateTooltipText}</HelpTooltipText>
|
||||
<HelpTooltipLinksGroup>
|
||||
<HelpTooltipLink href={docs("/templates")}>
|
||||
{Language.templateTooltipLink}
|
||||
</HelpTooltipLink>
|
||||
</HelpTooltipLinksGroup>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -8,8 +8,10 @@ import UserIcon from "@mui/icons-material/PersonOutline";
|
||||
import { Role } from "api/typesGenerated";
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import {
|
||||
Popover,
|
||||
@@ -101,11 +103,14 @@ export const EditRolesButton: FC<EditRolesButtonProps> = ({
|
||||
|
||||
if (!canSetRoles) {
|
||||
return (
|
||||
<HelpTooltip size="small">
|
||||
<HelpTooltipTitle>Externally controlled</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
Roles for this user are controlled by the OIDC identity provider.
|
||||
</HelpTooltipText>
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger size="small" />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>Externally controlled</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
Roles for this user are controlled by the OIDC identity provider.
|
||||
</HelpTooltipText>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { FC } from "react";
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipLink,
|
||||
HelpTooltipLinksGroup,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { docs } from "utils/docs";
|
||||
|
||||
@@ -42,17 +44,19 @@ export const TableColumnHelpTooltip: FC<Props> = ({ variant }) => {
|
||||
const variantLang = Language[variant];
|
||||
|
||||
return (
|
||||
<HelpTooltip size="small">
|
||||
<HelpTooltipTitle>{variantLang.title}</HelpTooltipTitle>
|
||||
<HelpTooltipText>{variantLang.text}</HelpTooltipText>
|
||||
|
||||
<HelpTooltipLinksGroup>
|
||||
{variantLang.links.map((link) => (
|
||||
<HelpTooltipLink key={link.text} href={link.href}>
|
||||
{link.text}
|
||||
</HelpTooltipLink>
|
||||
))}
|
||||
</HelpTooltipLinksGroup>
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger size="small" />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>{variantLang.title}</HelpTooltipTitle>
|
||||
<HelpTooltipText>{variantLang.text}</HelpTooltipText>
|
||||
<HelpTooltipLinksGroup>
|
||||
{variantLang.links.map((link) => (
|
||||
<HelpTooltipLink key={link.text} href={link.href}>
|
||||
{link.text}
|
||||
</HelpTooltipLink>
|
||||
))}
|
||||
</HelpTooltipLinksGroup>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -87,7 +87,7 @@ export const WorkspaceStats: FC<WorkspaceStatsProps> = ({
|
||||
css={styles.statsItem}
|
||||
label="Version"
|
||||
value={
|
||||
<>
|
||||
<span css={{ display: "flex", alignItems: "center", gap: 4 }}>
|
||||
<Link
|
||||
component={RouterLink}
|
||||
to={`/templates/${workspace.template_name}/versions/${workspace.latest_build.template_version_name}`}
|
||||
@@ -103,7 +103,7 @@ export const WorkspaceStats: FC<WorkspaceStatsProps> = ({
|
||||
ariaLabel="update version"
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { FC } from "react";
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipLink,
|
||||
HelpTooltipLinksGroup,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { docs } from "utils/docs";
|
||||
|
||||
@@ -20,16 +22,19 @@ const Language = {
|
||||
export const WorkspaceHelpTooltip: FC = () => {
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTitle>{Language.workspaceTooltipTitle}</HelpTooltipTitle>
|
||||
<HelpTooltipText>{Language.workspaceTooltipText}</HelpTooltipText>
|
||||
<HelpTooltipLinksGroup>
|
||||
<HelpTooltipLink href={docs("/workspaces")}>
|
||||
{Language.workspaceTooltipLink1}
|
||||
</HelpTooltipLink>
|
||||
<HelpTooltipLink href={docs("/ides")}>
|
||||
{Language.workspaceTooltipLink2}
|
||||
</HelpTooltipLink>
|
||||
</HelpTooltipLinksGroup>
|
||||
<HelpTooltipTrigger />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>{Language.workspaceTooltipTitle}</HelpTooltipTitle>
|
||||
<HelpTooltipText>{Language.workspaceTooltipText}</HelpTooltipText>
|
||||
<HelpTooltipLinksGroup>
|
||||
<HelpTooltipLink href={docs("/workspaces")}>
|
||||
{Language.workspaceTooltipLink1}
|
||||
</HelpTooltipLink>
|
||||
<HelpTooltipLink href={docs("/ides")}>
|
||||
{Language.workspaceTooltipLink2}
|
||||
</HelpTooltipLink>
|
||||
</HelpTooltipLinksGroup>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -146,7 +146,7 @@ export const WorkspacesTable: FC<WorkspacesTableProps> = ({
|
||||
title={
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={0}
|
||||
spacing={0.5}
|
||||
alignItems="center"
|
||||
>
|
||||
{workspace.name}
|
||||
|
||||
Reference in New Issue
Block a user