diff --git a/.server-changes/errors-page-polish-and-ga.md b/.server-changes/errors-page-polish-and-ga.md new file mode 100644 index 00000000000..6faa63d6942 --- /dev/null +++ b/.server-changes/errors-page-polish-and-ga.md @@ -0,0 +1,6 @@ +--- +area: webapp +type: feature +--- + +Ship the Errors page to all users, with a polish + bug-fix pass: pinned "No channel" item in the Slack alert channel picker, viewer-timezone alert timestamps via Slack's `` token, Activity sparkline peak tooltip, centered loading spinner and bug-icon empty state on the error detail page, ellipsis on the Configure alerts trigger. diff --git a/apps/webapp/app/components/errors/ConfigureErrorAlerts.tsx b/apps/webapp/app/components/errors/ConfigureErrorAlerts.tsx index dc586c89438..32ed778f877 100644 --- a/apps/webapp/app/components/errors/ConfigureErrorAlerts.tsx +++ b/apps/webapp/app/components/errors/ConfigureErrorAlerts.tsx @@ -196,7 +196,7 @@ export function ConfigureErrorAlerts({ name={slackChannel.name} placeholder={Select a Slack channel} heading="Filter channels…" - defaultValue={selectedSlackChannelValue} + value={selectedSlackChannelValue ?? ""} dropdownIcon variant="tertiary/medium" items={slack.channels} @@ -218,6 +218,15 @@ export function ConfigureErrorAlerts({ > {(matches) => ( <> + +
+ + No channel +
+
{matches?.map((channel) => ( )} - {(user.admin || user.isImpersonating) && ( - - )} + {ErrorId.toFriendlyId(fingerprint)}} /> + + + Configure alerts… + + -
+
+
Loading error details…
@@ -366,7 +380,6 @@ export default function Page() { projectParam={projectParam} envParam={envParam} fingerprint={fingerprint} - alertsHref={alertsHref} /> ); }} @@ -385,7 +398,6 @@ function ErrorGroupDetail({ projectParam, envParam, fingerprint, - alertsHref, }: { errorGroup: ErrorGroupSummary | undefined; runList: NextRunList | undefined; @@ -394,7 +406,6 @@ function ErrorGroupDetail({ projectParam: string; envParam: string; fingerprint: string; - alertsHref: string; }) { const { value, values } = useSearchParams(); const organization = useOrganization(); @@ -499,9 +510,12 @@ function ErrorGroupDetail({ additionalTableState={{ errorId: ErrorId.toFriendlyId(fingerprint) }} /> ) : ( - - No runs found for this error. - +
+ + + No runs found for this error. + +
)}
@@ -510,11 +524,7 @@ function ErrorGroupDetail({ {/* Right-hand detail sidebar */} - + ); @@ -523,24 +533,14 @@ function ErrorGroupDetail({ function ErrorDetailSidebar({ errorGroup, fingerprint, - alertsHref, }: { errorGroup: ErrorGroupSummary; fingerprint: string; - alertsHref: string; }) { return (
-
+
Details - - Configure alerts -
diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors._index/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors._index/route.tsx index e92b5b34644..103115e549b 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors._index/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors._index/route.tsx @@ -55,6 +55,7 @@ import { statusActionToastMessage, } from "~/components/errors/ErrorStatusMenu"; import { useToast } from "~/components/primitives/Toast"; +import { SimpleTooltip } from "~/components/primitives/Tooltip"; import TooltipPortal from "~/components/primitives/TooltipPortal"; import { appliedSummary, FilterMenuProvider, TimeFilter } from "~/components/runs/v3/SharedFilters"; import { $replica } from "~/db.server"; @@ -463,7 +464,7 @@ function FiltersBar({ LeadingIcon={BellAlertIcon} leadingIconClassName="text-alerts" > - Configure alerts + Configure alerts… {list && }
@@ -706,9 +707,14 @@ function ErrorActivityGraph({ activity }: { activity: ErrorOccurrenceActivity })
- - {formatNumberCompact(maxCount)} - + + {formatNumberCompact(maxCount)} + + } + content="Peak occurrences in a single time bucket" + />
); } diff --git a/apps/webapp/app/v3/services/alerts/deliverAlert.server.ts b/apps/webapp/app/v3/services/alerts/deliverAlert.server.ts index 5ab99bf8046..bc8f9a3a5f2 100644 --- a/apps/webapp/app/v3/services/alerts/deliverAlert.server.ts +++ b/apps/webapp/app/v3/services/alerts/deliverAlert.server.ts @@ -1182,15 +1182,19 @@ export class DeliverAlertService extends BaseService { } #formatTimestamp(date: Date): string { - return new Intl.DateTimeFormat("en-US", { - month: "short", - day: "numeric", - year: "numeric", - hour: "numeric", - minute: "2-digit", - second: "2-digit", - hour12: true, - }).format(date); + const unix = Math.floor(date.getTime() / 1000); + const fallback = + new Intl.DateTimeFormat("en-US", { + month: "short", + day: "numeric", + year: "numeric", + hour: "numeric", + minute: "2-digit", + second: "2-digit", + hour12: true, + timeZone: "UTC", + }).format(date) + " UTC"; + return ``; } #buildWebhookGitObject(git: GitMetaLinks | null) { diff --git a/apps/webapp/app/v3/services/alerts/deliverErrorGroupAlert.server.ts b/apps/webapp/app/v3/services/alerts/deliverErrorGroupAlert.server.ts index 422811cdee3..647a0b0cf39 100644 --- a/apps/webapp/app/v3/services/alerts/deliverErrorGroupAlert.server.ts +++ b/apps/webapp/app/v3/services/alerts/deliverErrorGroupAlert.server.ts @@ -383,15 +383,19 @@ export class DeliverErrorGroupAlertService { } #formatTimestamp(date: Date): string { - return new Intl.DateTimeFormat("en-US", { - month: "short", - day: "numeric", - year: "numeric", - hour: "numeric", - minute: "2-digit", - second: "2-digit", - hour12: true, - }).format(date); + const unix = Math.floor(date.getTime() / 1000); + const fallback = + new Intl.DateTimeFormat("en-US", { + month: "short", + day: "numeric", + year: "numeric", + hour: "numeric", + minute: "2-digit", + second: "2-digit", + hour12: true, + timeZone: "UTC", + }).format(date) + " UTC"; + return ``; } }