Skip to content

Commit 276dffc

Browse files
committed
😎
1 parent a20a570 commit 276dffc

File tree

6 files changed

+76
-74
lines changed

6 files changed

+76
-74
lines changed

site/src/pages/UserSettingsPage/AccountPage/AccountPage.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ describe("AccountPage", () => {
2929
Promise.resolve({
3030
id: userId,
3131
email: "user@coder.com",
32-
created_at: new Date().toString(),
32+
created_at: new Date().toISOString(),
3333
status: "active",
3434
organization_ids: ["123"],
3535
roles: [],
3636
avatar_url: "",
37-
last_seen_at: new Date().toString(),
37+
last_seen_at: new Date().toISOString(),
3838
login_type: "password",
3939
theme_preference: "",
4040
...data,
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { type FC } from "react";
2+
import dayjs from "dayjs";
3+
import relativeTime from "dayjs/plugin/relativeTime";
4+
import type { Workspace } from "api/typesGenerated";
5+
import { Pill } from "components/Pill/Pill";
6+
7+
dayjs.extend(relativeTime);
8+
9+
interface ActivityStatusProps {
10+
workspace: Workspace;
11+
}
12+
13+
export const ActivityStatus: FC<ActivityStatusProps> = ({ workspace }) => {
14+
const builtAt = dayjs(workspace.latest_build.created_at);
15+
const usedAt = dayjs(workspace.last_used_at);
16+
const now = dayjs();
17+
18+
// This needs to compare to `usedAt` instead of `now`, because the "grace period" for
19+
// marking a workspace as "Connected" is a lot longer. If you compared `builtAt` to `now`,
20+
// you could end up switching from "Ready" to "Connected" without ever actually connecting.
21+
const isBuiltRecently = builtAt.isAfter(usedAt.subtract(2, "minute"));
22+
const isUsedRecently = usedAt.isAfter(now.subtract(15, "minute"));
23+
24+
switch (workspace.latest_build.status) {
25+
// If the build is still "fresh", it'll be a while before the `last_used_at` gets bumped in
26+
// a significant way by the agent, so just label it as ready instead of connected.
27+
// Wait until `last_used_at` is at least 2 minutes after the build was created, _and_ still
28+
// make sure to check that it's recent.
29+
case isBuiltRecently &&
30+
isUsedRecently &&
31+
workspace.health.healthy &&
32+
"running":
33+
return <Pill type="active">Ready</Pill>;
34+
// Since the agent reports on a 10m interval, we present any connection within that period
35+
// plus a little wiggle room as an active connection.
36+
case usedAt.isAfter(now.subtract(15, "minute")) && "running":
37+
return <Pill type="active">Connected</Pill>;
38+
case "running":
39+
case "stopping":
40+
case "stopped":
41+
return <Pill type="inactive">Not connected</Pill>;
42+
}
43+
44+
return null;
45+
};

site/src/pages/WorkspacePage/ConnectionStatus.tsx

Lines changed: 0 additions & 68 deletions
This file was deleted.

site/src/pages/WorkspacePage/WorkspaceTopbar.stories.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,28 @@ export const Outdated: Story = {
4848
},
4949
};
5050

51+
export const Ready: Story = {
52+
args: {
53+
workspace: {
54+
...baseWorkspace,
55+
last_used_at: new Date().toISOString(),
56+
latest_build: {
57+
...baseWorkspace.latest_build,
58+
created_at: new Date().toISOString(),
59+
},
60+
},
61+
},
62+
};
63+
64+
export const Connected: Story = {
65+
args: {
66+
workspace: {
67+
...baseWorkspace,
68+
last_used_at: new Date().toISOString(),
69+
},
70+
},
71+
};
72+
5173
export const Dormant: Story = {
5274
args: {
5375
workspace: {

site/src/pages/WorkspacePage/WorkspaceTopbar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import {
3333
shouldDisplayScheduleControls,
3434
} from "./WorkspaceScheduleControls";
3535
import { WorkspacePermissions } from "./permissions";
36-
import { ConnectionStatus } from "./ConnectionStatus";
36+
import { ActivityStatus } from "./ActivityStatus";
3737

3838
export type WorkspaceError =
3939
| "getBuildsError"
@@ -201,7 +201,7 @@ export const WorkspaceTopbar: FC<WorkspaceProps> = ({
201201
</Popover>
202202
</TopbarData>
203203

204-
<ConnectionStatus lastUsedAt={workspace.last_used_at} />
204+
<ActivityStatus workspace={workspace} />
205205

206206
{shouldDisplayScheduleControls(workspace) && (
207207
<TopbarData>

site/src/utils/schedule.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import Link from "@mui/material/Link";
12
import cronstrue from "cronstrue";
23
import cronParser from "cron-parser";
34
import dayjs, { type Dayjs } from "dayjs";
@@ -6,7 +7,7 @@ import relativeTime from "dayjs/plugin/relativeTime";
67
import timezone from "dayjs/plugin/timezone";
78
import utc from "dayjs/plugin/utc";
89
import { type ReactNode } from "react";
9-
import { Link } from "react-router-dom";
10+
import { Link as RouterLink } from "react-router-dom";
1011
import type { Template, Workspace } from "api/typesGenerated";
1112
import { isWorkspaceOn } from "./workspace";
1213

@@ -120,7 +121,9 @@ export const autostopDisplay = (
120121
{" "}
121122
because this workspace has enabled autostop. You can disable it from
122123
the{" "}
123-
<Link to="settings/schedule">Workspace Schedule settings page</Link>
124+
<Link component={RouterLink} to="settings/schedule">
125+
Workspace Schedule settings page
126+
</Link>
124127
.
125128
</>
126129
);

0 commit comments

Comments
 (0)