Skip to content

refactor(site): refactor resource and agents #11647

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
06c9bef
Refactor resource metadata
BrunoQuaresma Jan 16, 2024
f2346e6
Fix scrolling
BrunoQuaresma Jan 16, 2024
2c58d16
Refactor agent apps
BrunoQuaresma Jan 16, 2024
0a2cb43
Refactor metadata and logs button
BrunoQuaresma Jan 16, 2024
7db1022
Refactor ssh and open port actions
BrunoQuaresma Jan 16, 2024
91fe9f2
Add terminal icon to the terminal button
BrunoQuaresma Jan 16, 2024
0438f3a
Add simple message for agentless resources
BrunoQuaresma Jan 16, 2024
c3411b3
Add back agent status
BrunoQuaresma Jan 16, 2024
bc1a216
Make agent header thiner
BrunoQuaresma Jan 16, 2024
0490e89
rowback the changes on latency color
BrunoQuaresma Jan 16, 2024
e4b2aa1
Remove non apps from preview
BrunoQuaresma Jan 16, 2024
2e542ca
rowback a few decisions
BrunoQuaresma Jan 16, 2024
2b270ec
Solve a few visual issues
BrunoQuaresma Jan 17, 2024
b0f773d
More visual fixes
BrunoQuaresma Jan 17, 2024
295f8f7
Refactor agent buttons and links
BrunoQuaresma Jan 17, 2024
9136c14
Merge branch 'main' of https://github.com/coder/coder into bq/imrefac…
BrunoQuaresma Jan 17, 2024
05fae9c
Fix conflict
BrunoQuaresma Jan 17, 2024
223f36b
Merge branch 'main' of https://github.com/coder/coder into bq/imrefac…
BrunoQuaresma Jan 18, 2024
f55397e
Remove unecessary checks
BrunoQuaresma Jan 18, 2024
f362353
Add agent row visibility test
BrunoQuaresma Jan 18, 2024
def0f70
Improve AppLink code structure
BrunoQuaresma Jan 18, 2024
0dba1cd
Add comment about icons spacing
BrunoQuaresma Jan 18, 2024
21b0c1f
Use right metadata reference
BrunoQuaresma Jan 18, 2024
d663f30
Avoid metadata mutation
BrunoQuaresma Jan 18, 2024
e8c7322
Improve 'p' component name
BrunoQuaresma Jan 18, 2024
90ce45a
Use better var name
BrunoQuaresma Jan 18, 2024
f8e56bc
Remove unecessary word
BrunoQuaresma Jan 18, 2024
5edc204
Improve nesting click handler
BrunoQuaresma Jan 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 10 additions & 14 deletions site/src/components/Resources/AgentButton.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,28 @@
import Button, { type ButtonProps } from "@mui/material/Button";
import { useTheme } from "@emotion/react";
import { forwardRef } from "react";

// eslint-disable-next-line react/display-name -- Name is inferred from variable name
export const AgentButton = forwardRef<HTMLButtonElement, ButtonProps>(
(props, ref) => {
const { children, ...buttonProps } = props;
const theme = useTheme();

return (
<Button
color="neutral"
{...buttonProps}
color="neutral"
size="xlarge"
variant="contained"
ref={ref}
css={{
backgroundColor: theme.palette.background.default,

"&:hover": {
backgroundColor: theme.palette.background.paper,
},

css={(theme) => ({
padding: "12px 20px",
color: theme.palette.text.primary,
// Making them smaller since those icons don't have a padding around them
"& .MuiButton-startIcon": {
width: 12,
height: 12,
"& .MuiButton-startIcon, & .MuiButton-endIcon": {
width: 16,
height: 16,
"& svg": { width: "100%", height: "100%" },
},
}}
})}
>
{children}
</Button>
Expand Down
179 changes: 83 additions & 96 deletions site/src/components/Resources/AgentMetadata.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,71 +24,6 @@ type ItemStatus = "stale" | "valid" | "loading";

export const WatchAgentMetadataContext = createContext(watchAgentMetadata);

interface MetadataItemProps {
item: WorkspaceAgentMetadata;
}

const MetadataItem: FC<MetadataItemProps> = ({ item }) => {
if (item.result === undefined) {
throw new Error("Metadata item result is undefined");
}
if (item.description === undefined) {
throw new Error("Metadata item description is undefined");
}

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" ? (
<Skeleton width={65} height={12} variant="text" css={styles.skeleton} />
) : status === "stale" ? (
<Tooltip title="This data is stale and no longer up to date">
<StaticWidth css={[styles.metadataValue, styles.metadataStale]}>
{item.result.value}
</StaticWidth>
</Tooltip>
) : (
<StaticWidth
css={[
styles.metadataValue,
item.result.error.length === 0
? styles.metadataValueSuccess
: styles.metadataValueError,
]}
>
{item.result.value}
</StaticWidth>
);

return (
<div css={styles.metadata}>
<div css={styles.metadataLabel}>{item.description.display_name}</div>
<div>{value}</div>
</div>
);
};

export interface AgentMetadataViewProps {
metadata: WorkspaceAgentMetadata[];
}
Expand All @@ -98,16 +33,11 @@ export const AgentMetadataView: FC<AgentMetadataViewProps> = ({ metadata }) => {
return null;
}
return (
<div css={styles.root}>
<Stack alignItems="baseline" direction="row" spacing={6}>
{metadata.map((m) => {
if (m.description === undefined) {
throw new Error("Metadata item description is undefined");
}
return <MetadataItem key={m.description.key} item={m} />;
})}
</Stack>
</div>
<section css={styles.root}>
{metadata.map((m) => (
<MetadataItem key={m.description.key} item={m} />
))}
</section>
);
};

Expand Down Expand Up @@ -162,13 +92,19 @@ export const AgentMetadata: FC<AgentMetadataProps> = ({

if (metadata === undefined) {
return (
<div css={styles.root}>
<section css={styles.root}>
<AgentMetadataSkeleton />
</div>
</section>
);
}

return <AgentMetadataView metadata={metadata} />;
return (
<AgentMetadataView
metadata={[...metadata].sort((a, b) =>
a.description.display_name.localeCompare(b.description.display_name),
)}
/>
);
};

export const AgentMetadataSkeleton: FC = () => {
Expand All @@ -192,6 +128,64 @@ export const AgentMetadataSkeleton: FC = () => {
);
};

interface MetadataItemProps {
item: WorkspaceAgentMetadata;
}

const MetadataItem: FC<MetadataItemProps> = ({ 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" ? (
<Skeleton width={65} height={12} variant="text" css={styles.skeleton} />
) : status === "stale" ? (
<Tooltip title="This data is stale and no longer up to date">
<StaticWidth css={[styles.metadataValue, styles.metadataStale]}>
{item.result.value}
</StaticWidth>
</Tooltip>
) : (
<StaticWidth
css={[
styles.metadataValue,
item.result.error.length === 0
? styles.metadataValueSuccess
: styles.metadataValueError,
]}
>
{item.result.value}
</StaticWidth>
);

return (
<div css={styles.metadata}>
<div css={styles.metadataLabel}>{item.description.display_name}</div>
<div>{value}</div>
</div>
);
};

const StaticWidth: FC<HTMLAttributes<HTMLDivElement>> = ({
children,
...attrs
Expand Down Expand Up @@ -221,33 +215,28 @@ const StaticWidth: FC<HTMLAttributes<HTMLDivElement>> = ({
// These are more or less copied from
// site/src/components/Resources/ResourceCard.tsx
const styles = {
root: (theme) => ({
padding: "20px 32px",
borderTop: `1px solid ${theme.palette.divider}`,
overflowX: "auto",
scrollPadding: "0 32px",
}),
root: {
display: "flex",
alignItems: "baseline",
flexWrap: "wrap",
gap: 32,
rowGap: 16,
},

metadata: {
fontSize: 12,
lineHeight: "normal",
lineHeight: "1.6",
display: "flex",
flexDirection: "column",
gap: 4,
overflow: "visible",

// Because of scrolling
"&:last-child": {
paddingRight: 32,
},
flexShrink: 0,
},

metadataLabel: (theme) => ({
color: theme.palette.text.secondary,
textOverflow: "ellipsis",
overflow: "hidden",
whiteSpace: "nowrap",
fontWeight: 500,
fontSize: 13,
}),

metadataValue: {
Expand All @@ -259,9 +248,7 @@ const styles = {
},

metadataValueSuccess: (theme) => ({
// color: theme.palette.success.light,
color: theme.experimental.roles.success.fill,
// color: theme.experimental.roles.success.text,
color: theme.experimental.roles.success.outline,
}),

metadataValueError: (theme) => ({
Expand Down
11 changes: 10 additions & 1 deletion site/src/components/Resources/AgentRow.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
MockWorkspaceAgentDeprecated,
MockWorkspaceApp,
MockProxyLatencies,
MockListeningPortsResponse,
} from "testHelpers/entities";
import { AgentRow, LineWithID } from "./AgentRow";
import { ProxyContext, getPreferredProxy } from "contexts/ProxyContext";
Expand Down Expand Up @@ -103,7 +104,15 @@ const storybookLogs: LineWithID[] = [

const meta: Meta<typeof AgentRow> = {
title: "components/AgentRow",
parameters: { chromatic },
parameters: {
chromatic,
queries: [
{
key: ["portForward", MockWorkspaceAgent.id],
data: MockListeningPortsResponse,
},
],
},
component: AgentRow,
args: {
storybookLogs,
Expand Down
Loading