Skip to content

[pull] main from coder:main #208

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 1 commit into from
May 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
31 changes: 13 additions & 18 deletions site/e2e/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ import type { Page } from "@playwright/test";
import { expect } from "@playwright/test";
import { API, type DeploymentConfig } from "api/api";
import type { SerpentOption } from "api/typesGenerated";
import { formatDuration, intervalToDuration } from "date-fns";
import dayjs from "dayjs";
import duration from "dayjs/plugin/duration";
import relativeTime from "dayjs/plugin/relativeTime";

dayjs.extend(duration);
dayjs.extend(relativeTime);
import { humanDuration } from "utils/time";
import { coderPort, defaultPassword } from "./constants";
import { type LoginOptions, findSessionToken, randomName } from "./helpers";

Expand Down Expand Up @@ -237,13 +243,6 @@ export async function verifyConfigFlagString(
await expect(configOption).toHaveText(opt.value as any);
}

export async function verifyConfigFlagEmpty(page: Page, flag: string) {
const configOption = page.locator(
`div.options-table .option-${flag} .option-value-empty`,
);
await expect(configOption).toHaveText("Not set");
}

export async function verifyConfigFlagArray(
page: Page,
config: DeploymentConfig,
Expand Down Expand Up @@ -290,19 +289,15 @@ export async function verifyConfigFlagDuration(
flag: string,
) {
const opt = findConfigOption(config, flag);
if (typeof opt.value !== "number") {
throw new Error(
`Option with env ${flag} should be a number, but got ${typeof opt.value}.`,
);
}
const configOption = page.locator(
`div.options-table .option-${flag} .option-value-string`,
);
//
await expect(configOption).toHaveText(
formatDuration(
// intervalToDuration takes ms, so convert nanoseconds to ms
intervalToDuration({
start: 0,
end: (opt.value as number) / 1e6,
}),
),
);
await expect(configOption).toHaveText(humanDuration(opt.value / 1e6));
}

export function findConfigOption(
Expand Down
7 changes: 5 additions & 2 deletions site/e2e/tests/deployment/observability.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
verifyConfigFlagArray,
verifyConfigFlagBoolean,
verifyConfigFlagDuration,
verifyConfigFlagEmpty,
verifyConfigFlagString,
} from "../../api";
import { login } from "../../helpers";
Expand All @@ -28,7 +27,11 @@ test("enabled observability settings", async ({ page }) => {
await verifyConfigFlagBoolean(page, config, "enable-terraform-debug-mode");
await verifyConfigFlagBoolean(page, config, "enable-terraform-debug-mode");
await verifyConfigFlagDuration(page, config, "health-check-refresh");
await verifyConfigFlagEmpty(page, "health-check-threshold-database");
await verifyConfigFlagDuration(
page,
config,
"health-check-threshold-database",
);
await verifyConfigFlagString(page, config, "log-human");
await verifyConfigFlagString(page, config, "prometheus-address");
await verifyConfigFlagArray(
Expand Down
3 changes: 2 additions & 1 deletion site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,12 @@
"color-convert": "2.0.1",
"cron-parser": "4.9.0",
"cronstrue": "2.50.0",
"date-fns": "2.30.0",
"dayjs": "1.11.13",
"emoji-mart": "5.6.0",
"file-saver": "2.0.5",
"formik": "2.4.6",
"front-matter": "4.0.2",
"humanize-duration": "3.32.2",
"jszip": "3.10.1",
"lodash": "4.17.21",
"lucide-react": "0.474.0",
Expand Down Expand Up @@ -149,6 +149,7 @@
"@types/color-convert": "2.0.4",
"@types/express": "4.17.17",
"@types/file-saver": "2.0.7",
"@types/humanize-duration": "3.27.4",
"@types/jest": "29.5.14",
"@types/lodash": "4.17.15",
"@types/node": "20.17.16",
Expand Down
19 changes: 16 additions & 3 deletions site/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 11 additions & 9 deletions site/src/components/LastSeen/LastSeen.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { useTheme } from "@emotion/react";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import type { FC, HTMLAttributes } from "react";
import { cn } from "utils/cn";

dayjs.extend(relativeTime);
import { isAfter, relativeTime, subtractTime } from "utils/time";

interface LastSeenProps
extends Omit<HTMLAttributes<HTMLSpanElement>, "children"> {
Expand All @@ -15,21 +13,25 @@ interface LastSeenProps
export const LastSeen: FC<LastSeenProps> = ({ at, className, ...attrs }) => {
const theme = useTheme();
const t = dayjs(at);
const now = dayjs();
const now = new Date();
const oneHourAgo = subtractTime(now, 1, "hour");
const threeDaysAgo = subtractTime(now, 3, "day");
const oneMonthAgo = subtractTime(now, 1, "month");
const centuryAgo = subtractTime(now, 100, "year");

let message = t.fromNow();
let message = relativeTime(at);
let color = theme.palette.text.secondary;

if (t.isAfter(now.subtract(1, "hour"))) {
if (isAfter(at, oneHourAgo)) {
// Since the agent reports on a 10m interval,
// the last_used_at can be inaccurate when recent.
message = "Now";
color = theme.roles.success.fill.solid;
} else if (t.isAfter(now.subtract(3, "day"))) {
} else if (isAfter(at, threeDaysAgo)) {
color = theme.experimental.l2.text;
} else if (t.isAfter(now.subtract(1, "month"))) {
} else if (isAfter(at, oneMonthAgo)) {
color = theme.roles.warning.fill.solid;
} else if (t.isAfter(now.subtract(100, "year"))) {
} else if (isAfter(at, centuryAgo)) {
color = theme.roles.error.fill.solid;
} else {
message = "Never";
Expand Down
15 changes: 11 additions & 4 deletions site/src/components/Timeline/utils.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import formatRelative from "date-fns/formatRelative";
import subDays from "date-fns/subDays";
import dayjs from "dayjs";
import calendar from "dayjs/plugin/calendar";

dayjs.extend(calendar);

export const createDisplayDate = (
date: Date,
base: Date = new Date(),
): string => {
const lastWeek = subDays(base, 7);
const lastWeek = dayjs(base).subtract(7, "day").toDate();
if (date >= lastWeek) {
return formatRelative(date, base).split(" at ")[0];
return dayjs(date).calendar(dayjs(base), {
sameDay: "[Today]",
lastDay: "[Yesterday]",
lastWeek: "[last] dddd",
sameElse: "MM/DD/YYYY",
});
}
return date.toLocaleDateString();
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import Tooltip from "@mui/material/Tooltip";
import type { Workspace } from "api/typesGenerated";
import { Badge } from "components/Badge/Badge";
import { formatDistanceToNow } from "date-fns";
import type { FC } from "react";
import {
DATE_FORMAT,
formatDateTime,
relativeTimeWithoutSuffix,
} from "utils/time";

export type WorkspaceDormantBadgeProps = {
workspace: Workspace;
Expand All @@ -11,25 +15,14 @@ export type WorkspaceDormantBadgeProps = {
export const WorkspaceDormantBadge: FC<WorkspaceDormantBadgeProps> = ({
workspace,
}) => {
const formatDate = (dateStr: string): string => {
const date = new Date(dateStr);
return date.toLocaleDateString(undefined, {
month: "long",
day: "numeric",
year: "numeric",
hour: "numeric",
minute: "numeric",
});
};

return workspace.deleting_at ? (
<Tooltip
title={
<>
This workspace has not been used for{" "}
{formatDistanceToNow(Date.parse(workspace.last_used_at))} and has been
{relativeTimeWithoutSuffix(workspace.last_used_at)} and has been
marked dormant. It is scheduled to be deleted on{" "}
{formatDate(workspace.deleting_at)}.
{formatDateTime(workspace.deleting_at, DATE_FORMAT.FULL_DATETIME)}.
</>
}
>
Expand All @@ -42,7 +35,7 @@ export const WorkspaceDormantBadge: FC<WorkspaceDormantBadgeProps> = ({
title={
<>
This workspace has not been used for{" "}
{formatDistanceToNow(Date.parse(workspace.last_used_at))} and has been
{relativeTimeWithoutSuffix(workspace.last_used_at)} and has been
marked dormant. It is not scheduled for auto-deletion but will become
a candidate if auto-deletion is enabled on this template.
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import type { GetLicensesResponse } from "api/api";
import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog";
import { Pill } from "components/Pill/Pill";
import { Stack } from "components/Stack/Stack";
import { compareAsc } from "date-fns";
import dayjs from "dayjs";
import { type FC, useState } from "react";

Expand Down Expand Up @@ -92,10 +91,7 @@ export const LicenseCard: FC<LicenseCardProps> = ({
alignItems="center"
width="134px" // standardize width of date column
>
{compareAsc(
new Date(license.claims.license_expires * 1000),
new Date(),
) < 1 ? (
{dayjs(license.claims.license_expires * 1000).isBefore(dayjs()) ? (
<Pill css={styles.expiredBadge} type="error">
Expired
</Pill>
Expand Down
10 changes: 2 additions & 8 deletions site/src/pages/DeploymentSettingsPage/optionValue.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { SerpentOption } from "api/typesGenerated";
import { formatDuration, intervalToDuration } from "date-fns";
import { humanDuration } from "utils/time";

// optionValue is a helper function to format the value of a specific deployment options
export function optionValue(
Expand All @@ -14,13 +14,7 @@ export function optionValue(
}
switch (k) {
case "format_duration":
return formatDuration(
// intervalToDuration takes ms, so convert nanoseconds to ms
intervalToDuration({
start: 0,
end: (option.value as number) / 1e6,
}),
);
return humanDuration((option.value as number) / 1e6);
// Add additional cases here as needed.
}
}
Expand Down
Loading
Loading