From e2020b2879cfa5c06df663ba67f9b259a0b0ecb8 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 26 Jan 2024 18:17:37 +0000 Subject: [PATCH 1/4] move components/Resources to modules/resources --- .../TemplateResourcesTable.tsx | 14 +- .../DeploymentBannerView.stories.tsx | 2 +- .../dashboard/Navbar/NavbarView.stories.tsx | 2 +- .../UserDropdown/UserDropdown.stories.tsx | 2 +- .../ServiceBannerView.stories.tsx | 2 +- .../resources}/AgentButton.tsx | 0 .../resources}/AgentLatency.tsx | 0 .../resources}/AgentMetadata.stories.tsx | 2 +- site/src/modules/resources/AgentMetadata.tsx | 275 ++++++++++++++++++ .../resources}/AgentOutdatedTooltip.tsx | 0 .../resources}/AgentRow.stories.tsx | 0 .../resources}/AgentRow.test.tsx | 0 .../resources}/AgentRow.tsx | 28 +- .../resources}/AgentRowPreview.stories.tsx | 2 +- .../resources}/AgentRowPreview.test.tsx | 0 .../resources}/AgentRowPreview.tsx | 8 +- .../resources}/AgentStatus.tsx | 0 .../resources}/AgentVersion.tsx | 0 .../resources}/AppLink/AppLink.stories.tsx | 2 +- .../resources}/AppLink/AppLink.tsx | 2 +- .../resources}/AppLink/AppPreview.tsx | 0 .../resources}/AppLink/BaseIcon.tsx | 0 .../resources}/AppLink/ShareIcon.tsx | 0 .../resources}/PortForwardButton.stories.tsx | 2 +- .../resources}/PortForwardButton.tsx | 0 .../PortForwardPopoverView.stories.tsx | 2 +- .../resources}/ResourceAvatar.stories.tsx | 2 +- .../resources}/ResourceAvatar.tsx | 0 .../resources}/ResourceCard.stories.tsx | 2 +- .../resources}/ResourceCard.test.tsx | 8 +- .../resources}/ResourceCard.tsx | 11 +- .../resources}/Resources.stories.tsx | 2 +- .../resources}/Resources.tsx | 9 +- .../SSHButton/SSHButton.stories.tsx | 2 +- .../resources}/SSHButton/SSHButton.tsx | 0 .../resources}/SensitiveValue.tsx | 0 .../TerminalLink/TerminalLink.stories.tsx | 2 +- .../resources}/TerminalLink/TerminalLink.tsx | 10 +- .../VSCodeDesktopButton.stories.tsx | 2 +- .../VSCodeDesktopButton.tsx | 4 +- .../pages/WorkspacePage/ResourceMetadata.tsx | 23 +- .../pages/WorkspacePage/Workspace.stories.tsx | 2 +- site/src/pages/WorkspacePage/Workspace.tsx | 2 +- 43 files changed, 351 insertions(+), 75 deletions(-) rename site/src/{components/Resources => modules/resources}/AgentButton.tsx (100%) rename site/src/{components/Resources => modules/resources}/AgentLatency.tsx (100%) rename site/src/{components/Resources => modules/resources}/AgentMetadata.stories.tsx (97%) create mode 100644 site/src/modules/resources/AgentMetadata.tsx rename site/src/{components/Resources => modules/resources}/AgentOutdatedTooltip.tsx (100%) rename site/src/{components/Resources => modules/resources}/AgentRow.stories.tsx (100%) rename site/src/{components/Resources => modules/resources}/AgentRow.test.tsx (100%) rename site/src/{components/Resources => modules/resources}/AgentRow.tsx (99%) rename site/src/{components/Resources => modules/resources}/AgentRowPreview.stories.tsx (94%) rename site/src/{components/Resources => modules/resources}/AgentRowPreview.test.tsx (100%) rename site/src/{components/Resources => modules/resources}/AgentRowPreview.tsx (99%) rename site/src/{components/Resources => modules/resources}/AgentStatus.tsx (100%) rename site/src/{components/Resources => modules/resources}/AgentVersion.tsx (100%) rename site/src/{components/Resources => modules/resources}/AppLink/AppLink.stories.tsx (98%) rename site/src/{components/Resources => modules/resources}/AppLink/AppLink.tsx (98%) rename site/src/{components/Resources => modules/resources}/AppLink/AppPreview.tsx (100%) rename site/src/{components/Resources => modules/resources}/AppLink/BaseIcon.tsx (100%) rename site/src/{components/Resources => modules/resources}/AppLink/ShareIcon.tsx (100%) rename site/src/{components/Resources => modules/resources}/PortForwardButton.stories.tsx (92%) rename site/src/{components/Resources => modules/resources}/PortForwardButton.tsx (100%) rename site/src/{components/Resources => modules/resources}/PortForwardPopoverView.stories.tsx (94%) rename site/src/{components/Resources => modules/resources}/ResourceAvatar.stories.tsx (96%) rename site/src/{components/Resources => modules/resources}/ResourceAvatar.tsx (100%) rename site/src/{components/Resources => modules/resources}/ResourceCard.stories.tsx (98%) rename site/src/{components/Resources => modules/resources}/ResourceCard.test.tsx (96%) rename site/src/{components/Resources => modules/resources}/ResourceCard.tsx (93%) rename site/src/{components/Resources => modules/resources}/Resources.stories.tsx (99%) rename site/src/{components/Resources => modules/resources}/Resources.tsx (91%) rename site/src/{components/Resources => modules/resources}/SSHButton/SSHButton.stories.tsx (94%) rename site/src/{components/Resources => modules/resources}/SSHButton/SSHButton.tsx (100%) rename site/src/{components/Resources => modules/resources}/SensitiveValue.tsx (100%) rename site/src/{components/Resources => modules/resources}/TerminalLink/TerminalLink.stories.tsx (90%) rename site/src/{components/Resources => modules/resources}/TerminalLink/TerminalLink.tsx (86%) rename site/src/{components/Resources => modules/resources}/VSCodeDesktopButton/VSCodeDesktopButton.stories.tsx (92%) rename site/src/{components/Resources => modules/resources}/VSCodeDesktopButton/VSCodeDesktopButton.tsx (97%) diff --git a/site/src/components/TemplateResourcesTable/TemplateResourcesTable.tsx b/site/src/components/TemplateResourcesTable/TemplateResourcesTable.tsx index 90562dbcbc51c..9d8e95114abb6 100644 --- a/site/src/components/TemplateResourcesTable/TemplateResourcesTable.tsx +++ b/site/src/components/TemplateResourcesTable/TemplateResourcesTable.tsx @@ -1,15 +1,15 @@ -import { AgentRowPreview } from "components/Resources/AgentRowPreview"; -import { Resources } from "components/Resources/Resources"; -import { FC } from "react"; -import { WorkspaceResource } from "api/typesGenerated"; +import { type FC } from "react"; +import type { WorkspaceResource } from "api/typesGenerated"; +import { AgentRowPreview } from "modules/resources/AgentRowPreview"; +import { Resources } from "modules/resources/Resources"; export interface TemplateResourcesProps { resources: WorkspaceResource[]; } -export const TemplateResourcesTable: FC< - React.PropsWithChildren -> = ({ resources }) => { +export const TemplateResourcesTable: FC = ({ + resources, +}) => { return ( = { - title: "components/DeploymentBannerView", + title: "modules/dashboard/DeploymentBannerView", component: DeploymentBannerView, args: { stats: MockDeploymentStats, diff --git a/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx b/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx index 93afc9c618622..fac1f344ce0c4 100644 --- a/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx +++ b/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx @@ -4,7 +4,7 @@ import { MockUser, MockUser2 } from "testHelpers/entities"; import { NavbarView } from "./NavbarView"; const meta: Meta = { - title: "components/NavbarView", + title: "modules/dashboard/NavbarView", parameters: { chromatic: chromaticWithTablet, layout: "fullscreen" }, component: NavbarView, args: { diff --git a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx index 75aceb5beea3d..9e25a272e32ae 100644 --- a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx +++ b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx @@ -3,7 +3,7 @@ import { UserDropdown } from "./UserDropdown"; import type { Meta, StoryObj } from "@storybook/react"; const meta: Meta = { - title: "components/UserDropdown", + title: "modules/dashboard/UserDropdown", component: UserDropdown, args: { user: MockUser, diff --git a/site/src/modules/dashboard/ServiceBanner/ServiceBannerView.stories.tsx b/site/src/modules/dashboard/ServiceBanner/ServiceBannerView.stories.tsx index 8f34bce0eec6a..1f3df18b3a42a 100644 --- a/site/src/modules/dashboard/ServiceBanner/ServiceBannerView.stories.tsx +++ b/site/src/modules/dashboard/ServiceBanner/ServiceBannerView.stories.tsx @@ -2,7 +2,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { ServiceBannerView } from "./ServiceBannerView"; const meta: Meta = { - title: "components/ServiceBannerView", + title: "modules/dashboard/ServiceBannerView", component: ServiceBannerView, }; diff --git a/site/src/components/Resources/AgentButton.tsx b/site/src/modules/resources/AgentButton.tsx similarity index 100% rename from site/src/components/Resources/AgentButton.tsx rename to site/src/modules/resources/AgentButton.tsx diff --git a/site/src/components/Resources/AgentLatency.tsx b/site/src/modules/resources/AgentLatency.tsx similarity index 100% rename from site/src/components/Resources/AgentLatency.tsx rename to site/src/modules/resources/AgentLatency.tsx diff --git a/site/src/components/Resources/AgentMetadata.stories.tsx b/site/src/modules/resources/AgentMetadata.stories.tsx similarity index 97% rename from site/src/components/Resources/AgentMetadata.stories.tsx rename to site/src/modules/resources/AgentMetadata.stories.tsx index 586944a655878..7382aac3de737 100644 --- a/site/src/components/Resources/AgentMetadata.stories.tsx +++ b/site/src/modules/resources/AgentMetadata.stories.tsx @@ -6,7 +6,7 @@ import { AgentMetadataView } from "./AgentMetadata"; import type { Meta, StoryObj } from "@storybook/react"; const meta: Meta = { - title: "components/AgentMetadataView", + title: "modules/resources/AgentMetadataView", component: AgentMetadataView, }; diff --git a/site/src/modules/resources/AgentMetadata.tsx b/site/src/modules/resources/AgentMetadata.tsx new file mode 100644 index 0000000000000..18099dd4ce815 --- /dev/null +++ b/site/src/modules/resources/AgentMetadata.tsx @@ -0,0 +1,275 @@ +import dayjs from "dayjs"; +import Skeleton from "@mui/material/Skeleton"; +import Tooltip from "@mui/material/Tooltip"; +import { type Interpolation, type Theme } from "@emotion/react"; +import { + createContext, + type FC, + type HTMLAttributes, + useContext, + useEffect, + useLayoutEffect, + useRef, + useState, +} from "react"; +import { watchAgentMetadata } from "api/api"; +import type { + WorkspaceAgent, + WorkspaceAgentMetadata, +} from "api/typesGenerated"; +import { Stack } from "components/Stack/Stack"; +import { MONOSPACE_FONT_FAMILY } from "theme/constants"; + +type ItemStatus = "stale" | "valid" | "loading"; + +export const WatchAgentMetadataContext = createContext(watchAgentMetadata); + +export interface AgentMetadataViewProps { + metadata: WorkspaceAgentMetadata[]; +} + +export const AgentMetadataView: FC = ({ metadata }) => { + if (metadata.length === 0) { + return null; + } + return ( +
+ {metadata.map((m) => ( + + ))} +
+ ); +}; + +interface AgentMetadataProps { + agent: WorkspaceAgent; + storybookMetadata?: WorkspaceAgentMetadata[]; +} + +export const AgentMetadata: FC = ({ + agent, + storybookMetadata, +}) => { + const [metadata, setMetadata] = useState< + WorkspaceAgentMetadata[] | undefined + >(undefined); + const watchAgentMetadata = useContext(WatchAgentMetadataContext); + + useEffect(() => { + if (storybookMetadata !== undefined) { + setMetadata(storybookMetadata); + return; + } + + let timeout: ReturnType | undefined = undefined; + + const connect = (): (() => void) => { + const source = watchAgentMetadata(agent.id); + + source.onerror = (e) => { + console.error("received error in watch stream", e); + setMetadata(undefined); + source.close(); + + timeout = setTimeout(() => { + connect(); + }, 3000); + }; + + source.addEventListener("data", (e) => { + const data = JSON.parse(e.data); + setMetadata(data); + }); + return () => { + if (timeout !== undefined) { + clearTimeout(timeout); + } + source.close(); + }; + }; + return connect(); + }, [agent.id, watchAgentMetadata, storybookMetadata]); + + if (metadata === undefined) { + return ( +
+ +
+ ); + } + + return ( + + a.description.display_name.localeCompare(b.description.display_name), + )} + /> + ); +}; + +export const AgentMetadataSkeleton: FC = () => { + return ( + +
+ + +
+ +
+ + +
+ +
+ + +
+
+ ); +}; + +interface MetadataItemProps { + item: WorkspaceAgentMetadata; +} + +const MetadataItem: FC = ({ item }) => { + const staleThreshold = Math.max( + item.description.interval + item.description.timeout * 2, + // In case there is intense backpressure, we give a little bit of slack. + 5, + ); + + const status: ItemStatus = (() => { + const year = dayjs(item.result.collected_at).year(); + if (year <= 1970 || isNaN(year)) { + return "loading"; + } + // There is a special circumstance for metadata with `interval: 0`. It is + // expected that they run once and never again, so never display them as + // stale. + if (item.result.age > staleThreshold && item.description.interval > 0) { + return "stale"; + } + return "valid"; + })(); + + // Stale data is as good as no data. Plus, we want to build confidence in our + // users that what's shown is real. If times aren't correctly synced this + // could be buggy. But, how common is that anyways? + const value = + status === "loading" ? ( + + ) : status === "stale" ? ( + + + {item.result.value} + + + ) : ( + + {item.result.value} + + ); + + return ( +
+
{item.description.display_name}
+
{value}
+
+ ); +}; + +const StaticWidth: FC> = ({ + children, + ...attrs +}) => { + const ref = useRef(null); + + useLayoutEffect(() => { + // Ignore this in storybook + if (!ref.current || process.env.STORYBOOK === "true") { + return; + } + + const currentWidth = ref.current.getBoundingClientRect().width; + ref.current.style.width = "auto"; + const autoWidth = ref.current.getBoundingClientRect().width; + ref.current.style.width = + autoWidth > currentWidth ? `${autoWidth}px` : `${currentWidth}px`; + }, [children]); + + return ( +
+ {children} +
+ ); +}; + +// These are more or less copied from +// site/src/components/Resources/ResourceCard.tsx +const styles = { + root: { + display: "flex", + alignItems: "baseline", + flexWrap: "wrap", + gap: 32, + rowGap: 16, + }, + + metadata: { + lineHeight: "1.6", + display: "flex", + flexDirection: "column", + overflow: "visible", + flexShrink: 0, + }, + + metadataLabel: (theme) => ({ + color: theme.palette.text.secondary, + textOverflow: "ellipsis", + overflow: "hidden", + whiteSpace: "nowrap", + fontSize: 13, + }), + + metadataValue: { + textOverflow: "ellipsis", + overflow: "hidden", + whiteSpace: "nowrap", + maxWidth: "16em", + fontSize: 14, + }, + + metadataValueSuccess: (theme) => ({ + color: theme.experimental.roles.success.fill.outline, + }), + + metadataValueError: (theme) => ({ + color: theme.palette.error.main, + }), + + metadataStale: (theme) => ({ + color: theme.palette.text.disabled, + cursor: "pointer", + }), + + skeleton: { + marginTop: 4, + }, + + inlineCommand: (theme) => ({ + fontFamily: MONOSPACE_FONT_FAMILY, + display: "inline-block", + fontWeight: 600, + margin: 0, + borderRadius: 4, + color: theme.palette.text.primary, + }), +} satisfies Record>; diff --git a/site/src/components/Resources/AgentOutdatedTooltip.tsx b/site/src/modules/resources/AgentOutdatedTooltip.tsx similarity index 100% rename from site/src/components/Resources/AgentOutdatedTooltip.tsx rename to site/src/modules/resources/AgentOutdatedTooltip.tsx diff --git a/site/src/components/Resources/AgentRow.stories.tsx b/site/src/modules/resources/AgentRow.stories.tsx similarity index 100% rename from site/src/components/Resources/AgentRow.stories.tsx rename to site/src/modules/resources/AgentRow.stories.tsx diff --git a/site/src/components/Resources/AgentRow.test.tsx b/site/src/modules/resources/AgentRow.test.tsx similarity index 100% rename from site/src/components/Resources/AgentRow.test.tsx rename to site/src/modules/resources/AgentRow.test.tsx diff --git a/site/src/components/Resources/AgentRow.tsx b/site/src/modules/resources/AgentRow.tsx similarity index 99% rename from site/src/components/Resources/AgentRow.tsx rename to site/src/modules/resources/AgentRow.tsx index c0663116435ff..e0a1e9f9f59da 100644 --- a/site/src/components/Resources/AgentRow.tsx +++ b/site/src/modules/resources/AgentRow.tsx @@ -2,6 +2,17 @@ import Collapse from "@mui/material/Collapse"; import Skeleton from "@mui/material/Skeleton"; import Tooltip from "@mui/material/Tooltip"; import { type Interpolation, type Theme } from "@emotion/react"; +import { + type FC, + useCallback, + useEffect, + useLayoutEffect, + useMemo, + useRef, + useState, +} from "react"; +import AutoSizer from "react-virtualized-auto-sizer"; +import { FixedSizeList as List, ListOnScrollProps } from "react-window"; import * as API from "api/api"; import type { Workspace, @@ -10,33 +21,22 @@ import type { WorkspaceAgentMetadata, } from "api/typesGenerated"; import { DropdownArrow } from "components/DropdownArrow/DropdownArrow"; -import { VSCodeDesktopButton } from "components/Resources/VSCodeDesktopButton/VSCodeDesktopButton"; import { Line, LogLine, logLineHeight, } from "components/WorkspaceBuildLogs/Logs"; import { useProxy } from "contexts/ProxyContext"; -import { - type FC, - useCallback, - useEffect, - useLayoutEffect, - useMemo, - useRef, - useState, -} from "react"; -import AutoSizer from "react-virtualized-auto-sizer"; -import { FixedSizeList as List, ListOnScrollProps } from "react-window"; -import { Stack } from "../Stack/Stack"; +import { Stack } from "components/Stack/Stack"; import { AgentLatency } from "./AgentLatency"; import { AgentMetadata } from "./AgentMetadata"; +import { AgentStatus } from "./AgentStatus"; import { AgentVersion } from "./AgentVersion"; import { AppLink } from "./AppLink/AppLink"; import { PortForwardButton } from "./PortForwardButton"; import { SSHButton } from "./SSHButton/SSHButton"; import { TerminalLink } from "./TerminalLink/TerminalLink"; -import { AgentStatus } from "./AgentStatus"; +import { VSCodeDesktopButton } from "./VSCodeDesktopButton/VSCodeDesktopButton"; // Logs are stored as the Line interface to make rendering // much more efficient. Instead of mapping objects each time, we're diff --git a/site/src/components/Resources/AgentRowPreview.stories.tsx b/site/src/modules/resources/AgentRowPreview.stories.tsx similarity index 94% rename from site/src/components/Resources/AgentRowPreview.stories.tsx rename to site/src/modules/resources/AgentRowPreview.stories.tsx index c14b43865ef98..efb93f474d3d5 100644 --- a/site/src/components/Resources/AgentRowPreview.stories.tsx +++ b/site/src/modules/resources/AgentRowPreview.stories.tsx @@ -3,7 +3,7 @@ import { AgentRowPreview } from "./AgentRowPreview"; import type { Meta, StoryObj } from "@storybook/react"; const meta: Meta = { - title: "components/AgentRowPreview", + title: "modules/resources/AgentRowPreview", component: AgentRowPreview, args: { agent: MockWorkspaceAgent, diff --git a/site/src/components/Resources/AgentRowPreview.test.tsx b/site/src/modules/resources/AgentRowPreview.test.tsx similarity index 100% rename from site/src/components/Resources/AgentRowPreview.test.tsx rename to site/src/modules/resources/AgentRowPreview.test.tsx diff --git a/site/src/components/Resources/AgentRowPreview.tsx b/site/src/modules/resources/AgentRowPreview.tsx similarity index 99% rename from site/src/components/Resources/AgentRowPreview.tsx rename to site/src/modules/resources/AgentRowPreview.tsx index f088b5ca77f08..6d99f2a6f25dd 100644 --- a/site/src/components/Resources/AgentRowPreview.tsx +++ b/site/src/modules/resources/AgentRowPreview.tsx @@ -1,12 +1,12 @@ import { type Interpolation, type Theme } from "@emotion/react"; import { type FC } from "react"; import type { WorkspaceAgent } from "api/typesGenerated"; -import { Stack } from "../Stack/Stack"; -import { AppPreview } from "./AppLink/AppPreview"; -import { BaseIcon } from "./AppLink/BaseIcon"; +import { Stack } from "components/Stack/Stack"; import { VSCodeIcon } from "components/Icons/VSCodeIcon"; -import { DisplayAppNameMap } from "./AppLink/AppLink"; import { TerminalIcon } from "components/Icons/TerminalIcon"; +import { DisplayAppNameMap } from "./AppLink/AppLink"; +import { AppPreview } from "./AppLink/AppPreview"; +import { BaseIcon } from "./AppLink/BaseIcon"; interface AgentRowPreviewStyles { // Helpful when there are more than one row so the values are aligned diff --git a/site/src/components/Resources/AgentStatus.tsx b/site/src/modules/resources/AgentStatus.tsx similarity index 100% rename from site/src/components/Resources/AgentStatus.tsx rename to site/src/modules/resources/AgentStatus.tsx diff --git a/site/src/components/Resources/AgentVersion.tsx b/site/src/modules/resources/AgentVersion.tsx similarity index 100% rename from site/src/components/Resources/AgentVersion.tsx rename to site/src/modules/resources/AgentVersion.tsx diff --git a/site/src/components/Resources/AppLink/AppLink.stories.tsx b/site/src/modules/resources/AppLink/AppLink.stories.tsx similarity index 98% rename from site/src/components/Resources/AppLink/AppLink.stories.tsx rename to site/src/modules/resources/AppLink/AppLink.stories.tsx index 4e71c244b5e63..7f77c2d8796e4 100644 --- a/site/src/components/Resources/AppLink/AppLink.stories.tsx +++ b/site/src/modules/resources/AppLink/AppLink.stories.tsx @@ -11,7 +11,7 @@ import { ProxyContext, getPreferredProxy } from "contexts/ProxyContext"; import type { Meta, StoryObj } from "@storybook/react"; const meta: Meta = { - title: "components/AppLink", + title: "modules/resources/AppLink", component: AppLink, decorators: [ (Story) => ( diff --git a/site/src/components/Resources/AppLink/AppLink.tsx b/site/src/modules/resources/AppLink/AppLink.tsx similarity index 98% rename from site/src/components/Resources/AppLink/AppLink.tsx rename to site/src/modules/resources/AppLink/AppLink.tsx index 75d03f6c477bc..4971946f683cf 100644 --- a/site/src/components/Resources/AppLink/AppLink.tsx +++ b/site/src/modules/resources/AppLink/AppLink.tsx @@ -7,9 +7,9 @@ import { useTheme } from "@emotion/react"; import { getApiKey } from "api/api"; import type * as TypesGen from "api/typesGenerated"; import { useProxy } from "contexts/ProxyContext"; -import { AgentButton } from "components/Resources/AgentButton"; import { createAppLinkHref } from "utils/apps"; import { generateRandomString } from "utils/random"; +import { AgentButton } from "../AgentButton"; import { BaseIcon } from "./BaseIcon"; import { ShareIcon } from "./ShareIcon"; diff --git a/site/src/components/Resources/AppLink/AppPreview.tsx b/site/src/modules/resources/AppLink/AppPreview.tsx similarity index 100% rename from site/src/components/Resources/AppLink/AppPreview.tsx rename to site/src/modules/resources/AppLink/AppPreview.tsx diff --git a/site/src/components/Resources/AppLink/BaseIcon.tsx b/site/src/modules/resources/AppLink/BaseIcon.tsx similarity index 100% rename from site/src/components/Resources/AppLink/BaseIcon.tsx rename to site/src/modules/resources/AppLink/BaseIcon.tsx diff --git a/site/src/components/Resources/AppLink/ShareIcon.tsx b/site/src/modules/resources/AppLink/ShareIcon.tsx similarity index 100% rename from site/src/components/Resources/AppLink/ShareIcon.tsx rename to site/src/modules/resources/AppLink/ShareIcon.tsx diff --git a/site/src/components/Resources/PortForwardButton.stories.tsx b/site/src/modules/resources/PortForwardButton.stories.tsx similarity index 92% rename from site/src/components/Resources/PortForwardButton.stories.tsx rename to site/src/modules/resources/PortForwardButton.stories.tsx index b2fc7ccfe704d..8ff4321c9135e 100644 --- a/site/src/components/Resources/PortForwardButton.stories.tsx +++ b/site/src/modules/resources/PortForwardButton.stories.tsx @@ -6,7 +6,7 @@ import { } from "testHelpers/entities"; const meta: Meta = { - title: "components/PortForwardButton", + title: "modules/resources/PortForwardButton", component: PortForwardButton, args: { agent: MockWorkspaceAgent, diff --git a/site/src/components/Resources/PortForwardButton.tsx b/site/src/modules/resources/PortForwardButton.tsx similarity index 100% rename from site/src/components/Resources/PortForwardButton.tsx rename to site/src/modules/resources/PortForwardButton.tsx diff --git a/site/src/components/Resources/PortForwardPopoverView.stories.tsx b/site/src/modules/resources/PortForwardPopoverView.stories.tsx similarity index 94% rename from site/src/components/Resources/PortForwardPopoverView.stories.tsx rename to site/src/modules/resources/PortForwardPopoverView.stories.tsx index 1f4c710e711a1..95b5e6a770580 100644 --- a/site/src/components/Resources/PortForwardPopoverView.stories.tsx +++ b/site/src/modules/resources/PortForwardPopoverView.stories.tsx @@ -6,7 +6,7 @@ import { } from "testHelpers/entities"; const meta: Meta = { - title: "components/PortForwardPopoverView", + title: "modules/resources/PortForwardPopoverView", component: PortForwardPopoverView, decorators: [ (Story) => ( diff --git a/site/src/components/Resources/ResourceAvatar.stories.tsx b/site/src/modules/resources/ResourceAvatar.stories.tsx similarity index 96% rename from site/src/components/Resources/ResourceAvatar.stories.tsx rename to site/src/modules/resources/ResourceAvatar.stories.tsx index 75af47063c532..96013aef5d7b2 100644 --- a/site/src/components/Resources/ResourceAvatar.stories.tsx +++ b/site/src/modules/resources/ResourceAvatar.stories.tsx @@ -3,7 +3,7 @@ import { ResourceAvatar } from "./ResourceAvatar"; import type { Meta, StoryObj } from "@storybook/react"; const meta: Meta = { - title: "components/ResourceAvatar", + title: "modules/resources/ResourceAvatar", component: ResourceAvatar, }; diff --git a/site/src/components/Resources/ResourceAvatar.tsx b/site/src/modules/resources/ResourceAvatar.tsx similarity index 100% rename from site/src/components/Resources/ResourceAvatar.tsx rename to site/src/modules/resources/ResourceAvatar.tsx diff --git a/site/src/components/Resources/ResourceCard.stories.tsx b/site/src/modules/resources/ResourceCard.stories.tsx similarity index 98% rename from site/src/components/Resources/ResourceCard.stories.tsx rename to site/src/modules/resources/ResourceCard.stories.tsx index 56c373c2081e8..c054223240bbd 100644 --- a/site/src/components/Resources/ResourceCard.stories.tsx +++ b/site/src/modules/resources/ResourceCard.stories.tsx @@ -9,7 +9,7 @@ import { type WorkspaceAgent } from "api/typesGenerated"; import { AgentRowPreview } from "./AgentRowPreview"; const meta: Meta = { - title: "components/Resources/ResourceCard", + title: "modules/resources/ResourceCard", component: ResourceCard, args: { resource: MockWorkspaceResource, diff --git a/site/src/components/Resources/ResourceCard.test.tsx b/site/src/modules/resources/ResourceCard.test.tsx similarity index 96% rename from site/src/components/Resources/ResourceCard.test.tsx rename to site/src/modules/resources/ResourceCard.test.tsx index 0e1a2cfb5ac67..f2882f365e438 100644 --- a/site/src/components/Resources/ResourceCard.test.tsx +++ b/site/src/modules/resources/ResourceCard.test.tsx @@ -1,8 +1,8 @@ -import { renderComponent } from "testHelpers/renderHelpers"; -import { ResourceCard } from "components/Resources/ResourceCard"; -import { MockWorkspaceResource } from "testHelpers/entities"; import { screen } from "@testing-library/react"; -import { WorkspaceResourceMetadata } from "api/typesGenerated"; +import type { WorkspaceResourceMetadata } from "api/typesGenerated"; +import { MockWorkspaceResource } from "testHelpers/entities"; +import { renderComponent } from "testHelpers/renderHelpers"; +import { ResourceCard } from "./ResourceCard"; describe("Resource Card", () => { it("renders daily cost and metadata tiles", async () => { diff --git a/site/src/components/Resources/ResourceCard.tsx b/site/src/modules/resources/ResourceCard.tsx similarity index 93% rename from site/src/components/Resources/ResourceCard.tsx rename to site/src/modules/resources/ResourceCard.tsx index 6e7188230a10d..1ab0bf14762d0 100644 --- a/site/src/components/Resources/ResourceCard.tsx +++ b/site/src/modules/resources/ResourceCard.tsx @@ -1,13 +1,12 @@ -import { type FC, type PropsWithChildren, useState } from "react"; import IconButton from "@mui/material/IconButton"; import Tooltip from "@mui/material/Tooltip"; import { type Interpolation, type Theme } from "@emotion/react"; -import { Children } from "react"; +import { Children, type FC, type PropsWithChildren, useState } from "react"; import type { WorkspaceAgent, WorkspaceResource } from "api/typesGenerated"; -import { DropdownArrow } from "../DropdownArrow/DropdownArrow"; -import { CopyableValue } from "../CopyableValue/CopyableValue"; -import { MemoizedInlineMarkdown } from "../Markdown/Markdown"; -import { Stack } from "../Stack/Stack"; +import { DropdownArrow } from "components/DropdownArrow/DropdownArrow"; +import { CopyableValue } from "components/CopyableValue/CopyableValue"; +import { MemoizedInlineMarkdown } from "components/Markdown/Markdown"; +import { Stack } from "components/Stack/Stack"; import { ResourceAvatar } from "./ResourceAvatar"; import { SensitiveValue } from "./SensitiveValue"; diff --git a/site/src/components/Resources/Resources.stories.tsx b/site/src/modules/resources/Resources.stories.tsx similarity index 99% rename from site/src/components/Resources/Resources.stories.tsx rename to site/src/modules/resources/Resources.stories.tsx index 8141b6516cc1d..f9e64f3801909 100644 --- a/site/src/components/Resources/Resources.stories.tsx +++ b/site/src/modules/resources/Resources.stories.tsx @@ -10,7 +10,7 @@ import { type WorkspaceAgent } from "api/typesGenerated"; import { AgentRowPreview } from "./AgentRowPreview"; const meta: Meta = { - title: "components/Resources/Resources", + title: "modules/resources/Resources", component: Resources, args: { resources: [MockWorkspaceResource], diff --git a/site/src/components/Resources/Resources.tsx b/site/src/modules/resources/Resources.tsx similarity index 91% rename from site/src/components/Resources/Resources.tsx rename to site/src/modules/resources/Resources.tsx index 556133506508c..7182d9d3f6586 100644 --- a/site/src/components/Resources/Resources.tsx +++ b/site/src/modules/resources/Resources.tsx @@ -1,11 +1,11 @@ import { type Interpolation, type Theme } from "@emotion/react"; import Button from "@mui/material/Button"; +import { useTheme } from "@emotion/react"; import { type FC, useState } from "react"; import type { WorkspaceAgent, WorkspaceResource } from "api/typesGenerated"; import { DropdownArrow } from "components/DropdownArrow/DropdownArrow"; -import { Stack } from "../Stack/Stack"; +import { Stack } from "components/Stack/Stack"; import { ResourceCard } from "./ResourceCard"; -import { useTheme } from "@mui/material/styles"; const countAgents = (resource: WorkspaceResource) => { return resource.agents ? resource.agents.length : 0; @@ -16,10 +16,7 @@ interface ResourcesProps { agentRow: (agent: WorkspaceAgent, numberOfAgents: number) => JSX.Element; } -export const Resources: FC> = ({ - resources, - agentRow, -}) => { +export const Resources: FC = ({ resources, agentRow }) => { const theme = useTheme(); const [shouldDisplayHideResources, setShouldDisplayHideResources] = useState(false); diff --git a/site/src/components/Resources/SSHButton/SSHButton.stories.tsx b/site/src/modules/resources/SSHButton/SSHButton.stories.tsx similarity index 94% rename from site/src/components/Resources/SSHButton/SSHButton.stories.tsx rename to site/src/modules/resources/SSHButton/SSHButton.stories.tsx index b5e451160fc60..7bc5f2a396878 100644 --- a/site/src/components/Resources/SSHButton/SSHButton.stories.tsx +++ b/site/src/modules/resources/SSHButton/SSHButton.stories.tsx @@ -3,7 +3,7 @@ import { SSHButton } from "./SSHButton"; import type { Meta, StoryObj } from "@storybook/react"; const meta: Meta = { - title: "components/SSHButton", + title: "modules/resources/SSHButton", component: SSHButton, }; diff --git a/site/src/components/Resources/SSHButton/SSHButton.tsx b/site/src/modules/resources/SSHButton/SSHButton.tsx similarity index 100% rename from site/src/components/Resources/SSHButton/SSHButton.tsx rename to site/src/modules/resources/SSHButton/SSHButton.tsx diff --git a/site/src/components/Resources/SensitiveValue.tsx b/site/src/modules/resources/SensitiveValue.tsx similarity index 100% rename from site/src/components/Resources/SensitiveValue.tsx rename to site/src/modules/resources/SensitiveValue.tsx diff --git a/site/src/components/Resources/TerminalLink/TerminalLink.stories.tsx b/site/src/modules/resources/TerminalLink/TerminalLink.stories.tsx similarity index 90% rename from site/src/components/Resources/TerminalLink/TerminalLink.stories.tsx rename to site/src/modules/resources/TerminalLink/TerminalLink.stories.tsx index ae44504e97f59..48a4d46d90132 100644 --- a/site/src/components/Resources/TerminalLink/TerminalLink.stories.tsx +++ b/site/src/modules/resources/TerminalLink/TerminalLink.stories.tsx @@ -3,7 +3,7 @@ import { TerminalLink } from "./TerminalLink"; import type { Meta, StoryObj } from "@storybook/react"; const meta: Meta = { - title: "components/TerminalLink", + title: "modules/resources/TerminalLink", component: TerminalLink, }; diff --git a/site/src/components/Resources/TerminalLink/TerminalLink.tsx b/site/src/modules/resources/TerminalLink/TerminalLink.tsx similarity index 86% rename from site/src/components/Resources/TerminalLink/TerminalLink.tsx rename to site/src/modules/resources/TerminalLink/TerminalLink.tsx index d73d7d5fe61cb..13bfb5b9381dd 100644 --- a/site/src/components/Resources/TerminalLink/TerminalLink.tsx +++ b/site/src/modules/resources/TerminalLink/TerminalLink.tsx @@ -1,10 +1,10 @@ import Link from "@mui/material/Link"; -import { AgentButton } from "components/Resources/AgentButton"; -import { FC } from "react"; -import * as TypesGen from "api/typesGenerated"; +import { type FC } from "react"; +import type * as TypesGen from "api/typesGenerated"; +import { TerminalIcon } from "components/Icons/TerminalIcon"; import { generateRandomString } from "utils/random"; import { DisplayAppNameMap } from "../AppLink/AppLink"; -import { TerminalIcon } from "components/Icons/TerminalIcon"; +import { AgentButton } from "../AgentButton"; export const Language = { terminalTitle: (identifier: string): string => `Terminal - ${identifier}`, @@ -23,7 +23,7 @@ export interface TerminalLinkProps { * If no user name is provided "me" is used however it makes the link not * shareable. */ -export const TerminalLink: FC> = ({ +export const TerminalLink: FC = ({ agentName, userName = "me", workspaceName, diff --git a/site/src/components/Resources/VSCodeDesktopButton/VSCodeDesktopButton.stories.tsx b/site/src/modules/resources/VSCodeDesktopButton/VSCodeDesktopButton.stories.tsx similarity index 92% rename from site/src/components/Resources/VSCodeDesktopButton/VSCodeDesktopButton.stories.tsx rename to site/src/modules/resources/VSCodeDesktopButton/VSCodeDesktopButton.stories.tsx index 4e23287d966bd..aef3c3933fbf8 100644 --- a/site/src/components/Resources/VSCodeDesktopButton/VSCodeDesktopButton.stories.tsx +++ b/site/src/modules/resources/VSCodeDesktopButton/VSCodeDesktopButton.stories.tsx @@ -3,7 +3,7 @@ import { VSCodeDesktopButton } from "./VSCodeDesktopButton"; import type { Meta, StoryObj } from "@storybook/react"; const meta: Meta = { - title: "components/VSCodeDesktopButton", + title: "modules/resources/VSCodeDesktopButton", component: VSCodeDesktopButton, }; diff --git a/site/src/components/Resources/VSCodeDesktopButton/VSCodeDesktopButton.tsx b/site/src/modules/resources/VSCodeDesktopButton/VSCodeDesktopButton.tsx similarity index 97% rename from site/src/components/Resources/VSCodeDesktopButton/VSCodeDesktopButton.tsx rename to site/src/modules/resources/VSCodeDesktopButton/VSCodeDesktopButton.tsx index ceb03f016e459..efbc9e32c6d1f 100644 --- a/site/src/components/Resources/VSCodeDesktopButton/VSCodeDesktopButton.tsx +++ b/site/src/modules/resources/VSCodeDesktopButton/VSCodeDesktopButton.tsx @@ -4,10 +4,10 @@ import Menu from "@mui/material/Menu"; import MenuItem from "@mui/material/MenuItem"; import { type FC, useState, useRef } from "react"; import { getApiKey } from "api/api"; -import { DisplayApp } from "api/typesGenerated"; +import type { DisplayApp } from "api/typesGenerated"; import { VSCodeIcon } from "components/Icons/VSCodeIcon"; import { VSCodeInsidersIcon } from "components/Icons/VSCodeInsidersIcon"; -import { AgentButton } from "components/Resources/AgentButton"; +import { AgentButton } from "../AgentButton"; import { DisplayAppNameMap } from "../AppLink/AppLink"; export interface VSCodeDesktopButtonProps { diff --git a/site/src/pages/WorkspacePage/ResourceMetadata.tsx b/site/src/pages/WorkspacePage/ResourceMetadata.tsx index bc7aeda23ed89..7fd1cd8524471 100644 --- a/site/src/pages/WorkspacePage/ResourceMetadata.tsx +++ b/site/src/pages/WorkspacePage/ResourceMetadata.tsx @@ -1,9 +1,14 @@ +import { type Interpolation, type Theme } from "@emotion/react"; +import { + Children, + type FC, + type HTMLAttributes, + type PropsWithChildren, +} from "react"; +import { SensitiveValue } from "modules/resources/SensitiveValue"; import { MemoizedInlineMarkdown } from "components/Markdown/Markdown"; -import { SensitiveValue } from "components/Resources/SensitiveValue"; import { CopyableValue } from "components/CopyableValue/CopyableValue"; -import { WorkspaceResource } from "api/typesGenerated"; -import { Children, FC, HTMLAttributes, PropsWithChildren } from "react"; -import { Interpolation, Theme } from "@emotion/react"; +import type { WorkspaceResource } from "api/typesGenerated"; type ResourceMetadataProps = Omit, "resource"> & { resource: WorkspaceResource; @@ -49,7 +54,7 @@ export const ResourceMetadata: FC = ({ ); }; -const MetaValue = ({ children }: PropsWithChildren) => { +const MetaValue: FC = ({ children }) => { const childrenArray = Children.toArray(children); if (childrenArray.every((child) => typeof child === "string")) { return ( @@ -71,9 +76,9 @@ const styles = { background: `linear-gradient(180deg, ${theme.palette.background.default} 25%, rgba(0, 0, 0, 0) 100%)`, }), - item: () => ({ + item: { lineHeight: "1.5", - }), + }, label: (theme) => ({ fontSize: 13, @@ -83,9 +88,9 @@ const styles = { whiteSpace: "nowrap", }), - value: () => ({ + value: { textOverflow: "ellipsis", overflow: "hidden", whiteSpace: "nowrap", - }), + }, } satisfies Record>; diff --git a/site/src/pages/WorkspacePage/Workspace.stories.tsx b/site/src/pages/WorkspacePage/Workspace.stories.tsx index 9c9b41dbf7416..2a289593acdca 100644 --- a/site/src/pages/WorkspacePage/Workspace.stories.tsx +++ b/site/src/pages/WorkspacePage/Workspace.stories.tsx @@ -6,7 +6,7 @@ import type { ProvisionerJobLog } from "api/typesGenerated"; import * as Mocks from "testHelpers/entities"; import { ProxyContext, getPreferredProxy } from "contexts/ProxyContext"; import { DashboardContext } from "modules/dashboard/DashboardProvider"; -import { WatchAgentMetadataContext } from "components/Resources/AgentMetadata"; +import { WatchAgentMetadataContext } from "modules/resources/AgentMetadata"; import { Workspace } from "./Workspace"; import { WorkspaceBuildLogsSection } from "./WorkspaceBuildLogsSection"; import { WorkspacePermissions } from "./permissions"; diff --git a/site/src/pages/WorkspacePage/Workspace.tsx b/site/src/pages/WorkspacePage/Workspace.tsx index 4ecf95bc2527f..b3fb4d1d6e1ee 100644 --- a/site/src/pages/WorkspacePage/Workspace.tsx +++ b/site/src/pages/WorkspacePage/Workspace.tsx @@ -5,7 +5,7 @@ import { type FC } from "react"; import { useNavigate } from "react-router-dom"; import type * as TypesGen from "api/typesGenerated"; import { Alert, AlertDetail } from "components/Alert/Alert"; -import { AgentRow } from "components/Resources/AgentRow"; +import { AgentRow } from "modules/resources/AgentRow"; import { useTab } from "hooks"; import { ActiveTransition, From d8efe91ab2b3c64cc3175ba8effbd99a310477bc Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 26 Jan 2024 21:32:08 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=8D=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WorkspaceScheduleControls.tsx | 13 +++++++------ .../WorkspaceSchedulePage.tsx | 6 +----- site/src/utils/schedule.ts | 18 +++++++++++++----- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/site/src/pages/WorkspacePage/WorkspaceScheduleControls.tsx b/site/src/pages/WorkspacePage/WorkspaceScheduleControls.tsx index 3327c3035ada6..eb898b93cc6f1 100644 --- a/site/src/pages/WorkspacePage/WorkspaceScheduleControls.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceScheduleControls.tsx @@ -245,13 +245,14 @@ const AutoStopDisplay: FC = ({ workspace }) => { return ( ({ - color: isShutdownSoon(workspace) - ? `${theme.palette.warning.light} !important` - : undefined, - })} + css={ + isShutdownSoon(workspace) && + ((theme) => ({ + color: `${theme.palette.warning.light} !important`, + })) + } > - Stop {display.message} + {display.message} ); diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx index 2c1eefc545aa0..49153a37e38b2 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx @@ -80,11 +80,7 @@ export const WorkspaceSchedulePage: FC = () => { {pageTitle([workspaceName, "Schedule"])} - + Workspace Schedule diff --git a/site/src/utils/schedule.ts b/site/src/utils/schedule.ts index c2a9a77f9e4ab..9f01b41482927 100644 --- a/site/src/utils/schedule.ts +++ b/site/src/utils/schedule.ts @@ -1,12 +1,13 @@ import cronstrue from "cronstrue"; -import dayjs, { Dayjs } from "dayjs"; +import cronParser from "cron-parser"; +import dayjs, { type Dayjs } from "dayjs"; import duration from "dayjs/plugin/duration"; import relativeTime from "dayjs/plugin/relativeTime"; import timezone from "dayjs/plugin/timezone"; import utc from "dayjs/plugin/utc"; -import { Template, Workspace } from "api/typesGenerated"; +import { type ReactNode } from "react"; +import type { Template, Workspace } from "api/typesGenerated"; import { isWorkspaceOn } from "./workspace"; -import cronParser from "cron-parser"; // REMARK: some plugins depend on utc, so it's listed first. Otherwise they're // sorted alphabetically. @@ -91,11 +92,18 @@ export const isShuttingDown = ( export const autostopDisplay = ( workspace: Workspace, ): { - message: string; + message: ReactNode; tooltip?: string; } => { const ttl = workspace.ttl_ms; + if (isWorkspaceOn(workspace) && true) { + return { + message: "Connected with SSH", + tooltip: "Workspace will not stop while there is an active connection", + }; + } + if (isWorkspaceOn(workspace) && workspace.latest_build.deadline) { // Workspace is on --> derive from latest_build.deadline. Note that the // user may modify their workspace object (ttl) while the workspace is @@ -111,7 +119,7 @@ export const autostopDisplay = ( } else { const deadlineTz = deadline.tz(dayjs.tz.guess()); return { - message: deadlineTz.fromNow(), + message: `Stop ${deadlineTz.fromNow()}`, tooltip: deadlineTz.format("MMMM D, YYYY h:mm A"), }; } From c958b2565b138d0141d53ee974365b29758a68bc Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 26 Jan 2024 21:48:58 +0000 Subject: [PATCH 3/4] =?UTF-8?q?Revert=20"=F0=9F=8D=92"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit d8efe91ab2b3c64cc3175ba8effbd99a310477bc. --- .../WorkspaceScheduleControls.tsx | 13 ++++++------- .../WorkspaceSchedulePage.tsx | 6 +++++- site/src/utils/schedule.ts | 18 +++++------------- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/site/src/pages/WorkspacePage/WorkspaceScheduleControls.tsx b/site/src/pages/WorkspacePage/WorkspaceScheduleControls.tsx index eb898b93cc6f1..3327c3035ada6 100644 --- a/site/src/pages/WorkspacePage/WorkspaceScheduleControls.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceScheduleControls.tsx @@ -245,14 +245,13 @@ const AutoStopDisplay: FC = ({ workspace }) => { return ( ({ - color: `${theme.palette.warning.light} !important`, - })) - } + css={(theme) => ({ + color: isShutdownSoon(workspace) + ? `${theme.palette.warning.light} !important` + : undefined, + })} > - {display.message} + Stop {display.message} ); diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx index 49153a37e38b2..2c1eefc545aa0 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx @@ -80,7 +80,11 @@ export const WorkspaceSchedulePage: FC = () => { {pageTitle([workspaceName, "Schedule"])} - + Workspace Schedule diff --git a/site/src/utils/schedule.ts b/site/src/utils/schedule.ts index 9f01b41482927..c2a9a77f9e4ab 100644 --- a/site/src/utils/schedule.ts +++ b/site/src/utils/schedule.ts @@ -1,13 +1,12 @@ import cronstrue from "cronstrue"; -import cronParser from "cron-parser"; -import dayjs, { type Dayjs } from "dayjs"; +import dayjs, { Dayjs } from "dayjs"; import duration from "dayjs/plugin/duration"; import relativeTime from "dayjs/plugin/relativeTime"; import timezone from "dayjs/plugin/timezone"; import utc from "dayjs/plugin/utc"; -import { type ReactNode } from "react"; -import type { Template, Workspace } from "api/typesGenerated"; +import { Template, Workspace } from "api/typesGenerated"; import { isWorkspaceOn } from "./workspace"; +import cronParser from "cron-parser"; // REMARK: some plugins depend on utc, so it's listed first. Otherwise they're // sorted alphabetically. @@ -92,18 +91,11 @@ export const isShuttingDown = ( export const autostopDisplay = ( workspace: Workspace, ): { - message: ReactNode; + message: string; tooltip?: string; } => { const ttl = workspace.ttl_ms; - if (isWorkspaceOn(workspace) && true) { - return { - message: "Connected with SSH", - tooltip: "Workspace will not stop while there is an active connection", - }; - } - if (isWorkspaceOn(workspace) && workspace.latest_build.deadline) { // Workspace is on --> derive from latest_build.deadline. Note that the // user may modify their workspace object (ttl) while the workspace is @@ -119,7 +111,7 @@ export const autostopDisplay = ( } else { const deadlineTz = deadline.tz(dayjs.tz.guess()); return { - message: `Stop ${deadlineTz.fromNow()}`, + message: deadlineTz.fromNow(), tooltip: deadlineTz.format("MMMM D, YYYY h:mm A"), }; } From ea6ac40769de9eeb9f4bcac613e029bec0e8ae4a Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 26 Jan 2024 22:01:57 +0000 Subject: [PATCH 4/4] oh --- site/.storybook/preview.jsx | 2 +- .../components/Resources/AgentMetadata.tsx | 275 ------------------ site/src/modules/resources/AgentMetadata.tsx | 2 +- site/src/modules/resources/AgentRow.test.tsx | 2 +- .../ProvisionerTagsPopover.stories.tsx | 8 +- 5 files changed, 8 insertions(+), 281 deletions(-) delete mode 100644 site/src/components/Resources/AgentMetadata.tsx diff --git a/site/.storybook/preview.jsx b/site/.storybook/preview.jsx index 54e2c3f2f7b1d..5c5d10023881b 100644 --- a/site/.storybook/preview.jsx +++ b/site/.storybook/preview.jsx @@ -45,7 +45,7 @@ export const parameters = { options: { storySort: { method: "alphabetical", - order: ["design", "pages", "components"], + order: ["design", "pages", "modules", "components"], locales: "en-US", }, }, diff --git a/site/src/components/Resources/AgentMetadata.tsx b/site/src/components/Resources/AgentMetadata.tsx deleted file mode 100644 index 18099dd4ce815..0000000000000 --- a/site/src/components/Resources/AgentMetadata.tsx +++ /dev/null @@ -1,275 +0,0 @@ -import dayjs from "dayjs"; -import Skeleton from "@mui/material/Skeleton"; -import Tooltip from "@mui/material/Tooltip"; -import { type Interpolation, type Theme } from "@emotion/react"; -import { - createContext, - type FC, - type HTMLAttributes, - useContext, - useEffect, - useLayoutEffect, - useRef, - useState, -} from "react"; -import { watchAgentMetadata } from "api/api"; -import type { - WorkspaceAgent, - WorkspaceAgentMetadata, -} from "api/typesGenerated"; -import { Stack } from "components/Stack/Stack"; -import { MONOSPACE_FONT_FAMILY } from "theme/constants"; - -type ItemStatus = "stale" | "valid" | "loading"; - -export const WatchAgentMetadataContext = createContext(watchAgentMetadata); - -export interface AgentMetadataViewProps { - metadata: WorkspaceAgentMetadata[]; -} - -export const AgentMetadataView: FC = ({ metadata }) => { - if (metadata.length === 0) { - return null; - } - return ( -
- {metadata.map((m) => ( - - ))} -
- ); -}; - -interface AgentMetadataProps { - agent: WorkspaceAgent; - storybookMetadata?: WorkspaceAgentMetadata[]; -} - -export const AgentMetadata: FC = ({ - agent, - storybookMetadata, -}) => { - const [metadata, setMetadata] = useState< - WorkspaceAgentMetadata[] | undefined - >(undefined); - const watchAgentMetadata = useContext(WatchAgentMetadataContext); - - useEffect(() => { - if (storybookMetadata !== undefined) { - setMetadata(storybookMetadata); - return; - } - - let timeout: ReturnType | undefined = undefined; - - const connect = (): (() => void) => { - const source = watchAgentMetadata(agent.id); - - source.onerror = (e) => { - console.error("received error in watch stream", e); - setMetadata(undefined); - source.close(); - - timeout = setTimeout(() => { - connect(); - }, 3000); - }; - - source.addEventListener("data", (e) => { - const data = JSON.parse(e.data); - setMetadata(data); - }); - return () => { - if (timeout !== undefined) { - clearTimeout(timeout); - } - source.close(); - }; - }; - return connect(); - }, [agent.id, watchAgentMetadata, storybookMetadata]); - - if (metadata === undefined) { - return ( -
- -
- ); - } - - return ( - - a.description.display_name.localeCompare(b.description.display_name), - )} - /> - ); -}; - -export const AgentMetadataSkeleton: FC = () => { - return ( - -
- - -
- -
- - -
- -
- - -
-
- ); -}; - -interface MetadataItemProps { - item: WorkspaceAgentMetadata; -} - -const MetadataItem: FC = ({ item }) => { - const staleThreshold = Math.max( - item.description.interval + item.description.timeout * 2, - // In case there is intense backpressure, we give a little bit of slack. - 5, - ); - - const status: ItemStatus = (() => { - const year = dayjs(item.result.collected_at).year(); - if (year <= 1970 || isNaN(year)) { - return "loading"; - } - // There is a special circumstance for metadata with `interval: 0`. It is - // expected that they run once and never again, so never display them as - // stale. - if (item.result.age > staleThreshold && item.description.interval > 0) { - return "stale"; - } - return "valid"; - })(); - - // Stale data is as good as no data. Plus, we want to build confidence in our - // users that what's shown is real. If times aren't correctly synced this - // could be buggy. But, how common is that anyways? - const value = - status === "loading" ? ( - - ) : status === "stale" ? ( - - - {item.result.value} - - - ) : ( - - {item.result.value} - - ); - - return ( -
-
{item.description.display_name}
-
{value}
-
- ); -}; - -const StaticWidth: FC> = ({ - children, - ...attrs -}) => { - const ref = useRef(null); - - useLayoutEffect(() => { - // Ignore this in storybook - if (!ref.current || process.env.STORYBOOK === "true") { - return; - } - - const currentWidth = ref.current.getBoundingClientRect().width; - ref.current.style.width = "auto"; - const autoWidth = ref.current.getBoundingClientRect().width; - ref.current.style.width = - autoWidth > currentWidth ? `${autoWidth}px` : `${currentWidth}px`; - }, [children]); - - return ( -
- {children} -
- ); -}; - -// These are more or less copied from -// site/src/components/Resources/ResourceCard.tsx -const styles = { - root: { - display: "flex", - alignItems: "baseline", - flexWrap: "wrap", - gap: 32, - rowGap: 16, - }, - - metadata: { - lineHeight: "1.6", - display: "flex", - flexDirection: "column", - overflow: "visible", - flexShrink: 0, - }, - - metadataLabel: (theme) => ({ - color: theme.palette.text.secondary, - textOverflow: "ellipsis", - overflow: "hidden", - whiteSpace: "nowrap", - fontSize: 13, - }), - - metadataValue: { - textOverflow: "ellipsis", - overflow: "hidden", - whiteSpace: "nowrap", - maxWidth: "16em", - fontSize: 14, - }, - - metadataValueSuccess: (theme) => ({ - color: theme.experimental.roles.success.fill.outline, - }), - - metadataValueError: (theme) => ({ - color: theme.palette.error.main, - }), - - metadataStale: (theme) => ({ - color: theme.palette.text.disabled, - cursor: "pointer", - }), - - skeleton: { - marginTop: 4, - }, - - inlineCommand: (theme) => ({ - fontFamily: MONOSPACE_FONT_FAMILY, - display: "inline-block", - fontWeight: 600, - margin: 0, - borderRadius: 4, - color: theme.palette.text.primary, - }), -} satisfies Record>; diff --git a/site/src/modules/resources/AgentMetadata.tsx b/site/src/modules/resources/AgentMetadata.tsx index 18099dd4ce815..e325ee3ef31cf 100644 --- a/site/src/modules/resources/AgentMetadata.tsx +++ b/site/src/modules/resources/AgentMetadata.tsx @@ -213,7 +213,7 @@ const StaticWidth: FC> = ({ }; // These are more or less copied from -// site/src/components/Resources/ResourceCard.tsx +// site/src/modules/resources/ResourceCard.tsx const styles = { root: { display: "flex", diff --git a/site/src/modules/resources/AgentRow.test.tsx b/site/src/modules/resources/AgentRow.test.tsx index bdedcce222fb7..77260083279f4 100644 --- a/site/src/modules/resources/AgentRow.test.tsx +++ b/site/src/modules/resources/AgentRow.test.tsx @@ -7,7 +7,7 @@ import { waitForLoaderToBeRemoved, } from "testHelpers/renderHelpers"; -jest.mock("components/Resources/AgentMetadata", () => { +jest.mock("modules/resources/AgentMetadata", () => { const AgentMetadata = () => <>; return { AgentMetadata }; }); diff --git a/site/src/pages/TemplateVersionEditorPage/ProvisionerTagsPopover.stories.tsx b/site/src/pages/TemplateVersionEditorPage/ProvisionerTagsPopover.stories.tsx index 664fce53fe260..818f92fac6531 100644 --- a/site/src/pages/TemplateVersionEditorPage/ProvisionerTagsPopover.stories.tsx +++ b/site/src/pages/TemplateVersionEditorPage/ProvisionerTagsPopover.stories.tsx @@ -1,11 +1,11 @@ import type { Meta, StoryObj } from "@storybook/react"; +import { useArgs } from "@storybook/preview-api"; import { chromatic } from "testHelpers/chromatic"; import { MockTemplateVersion } from "testHelpers/entities"; import { ProvisionerTagsPopover } from "./ProvisionerTagsPopover"; -import { useArgs } from "@storybook/preview-api"; const meta: Meta = { - title: "component/ProvisionerTagsPopover", + title: "pages/TemplateVersionEditorPage/ProvisionerTagsPopover", parameters: { chromatic, layout: "centered", @@ -37,4 +37,6 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Example: Story = {}; +const Example: Story = {}; + +export { Example as ProvisionerTagsPopover };