fix(web): make the re-auth banner a centered popup, not a full-width top bar

Unrelated to #2108's diagnostics work, folded in at the maintainer's request
rather than tracked separately.

The sticky full-bleed bar cost the whole view a band of vertical space for what
is a notification about one server, and it pushed down the monitoring sidebar
that an OAuth failure now opens. Every corner is spoken for — the toast stack
owns bottom-right, the right edge is the sidebar whose toolbar an anchored card
covers, top-left is the Servers header — so it is centered instead, which is
the one placement that reads as addressed to the window rather than attached to
the wrong panel.

Deliberately WITHOUT an overlay. This is a notification, not a decision that
must be made now, and blocking the page would force a choice between reading
the requests that explain the failure and keeping the affordance — dismissing
is not free, since "Authorize again" also clears the stale OAuth state, which a
plain reconnect does not do.

Kept as the existing declarative component rather than routed through Mantine's
`notifications`: that module is mocked in App.test.tsx, so switching would have
broken the ten tests asserting on this banner's text and its action button,
including the lost-authorization-state coverage. The message now stacks above a
right-aligned action, which a ~420px card needs.

Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-24 23:16:44 -04:00
commit d6250d6b05

View file

@ -1,4 +1,4 @@
import { Alert, Button, Group, Text } from "@mantine/core";
import { Alert, Button, Group, Stack, Text } from "@mantine/core";
export const DEFAULT_REAUTH_BANNER_TITLE = "Re-authentication required";
export const DEFAULT_REAUTH_BANNER_ACTION_LABEL = "Re-authenticate";
@ -24,11 +24,16 @@ const ReAuthAlert = Alert.withProps({
closeButtonLabel: "Dismiss",
});
const BannerRow = Group.withProps({
justify: "space-between",
align: "center",
wrap: "nowrap",
gap: "md",
// Message above the action rather than beside it: this renders in a ~420px
// popup, where a side-by-side row squeezes the message into a few words per
// line while the button keeps its full width.
const BannerBody = Stack.withProps({
gap: "xs",
});
const BannerActions = Group.withProps({
justify: "flex-end",
gap: "xs",
});
const MessageText = Text.withProps({
@ -50,10 +55,12 @@ export function ReAuthBanner({
}: ReAuthBannerProps) {
return (
<ReAuthAlert title={title} onClose={onDismiss}>
<BannerRow>
<BannerBody>
<MessageText>{message}</MessageText>
<ReAuthButton onClick={onReauthenticate}>{actionLabel}</ReAuthButton>
</BannerRow>
<BannerActions>
<ReAuthButton onClick={onReauthenticate}>{actionLabel}</ReAuthButton>
</BannerActions>
</BannerBody>
</ReAuthAlert>
);
}