DialogContent is a CSS grid, and an implicit grid track has a min-content
floor. `break-words` does not reduce min-content size, so a long
unbreakable URL in any child pushed every child past the card's
max-width instead of wrapping. In message history that left the message
box, the delivery error panel and the action button rendered hundreds of
pixels outside the dialog, off the side of the screen.
An explicit minmax(0,1fr) column removes that floor, after which
break-words can do its job. This fixes every dialog in the app, not just
this one: any long URL, API key or gateway ID hit the same track.
Also makes the details dialog the single scroll container, matching
api-keys and the webhook payload modal, instead of capping the message
body at 224px behind a scrollbar nested in a scrollbar. And the list row
preview now wraps, so a URL-heavy message fills both clamped lines
rather than being cut off partway through the first.
Covered by an e2e regression test at 375px and 1280px that asserts the
dialog does not scroll sideways and that the action button stays inside
it. Verified failing before this change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Day headers in message history could cover message rows. Two distinct causes,
both reproduced before fixing:
- On mobile the header was pinned to top-14, the same sticky band the mobile
search bar already occupies, so it detached and landed on top of rows.
Being bg-muted/70 with a backdrop blur, row text bled through it, which is
the distortion that was reported.
- On desktop it was pinned to top-0, behind the app header, and it covered
rows scrolled beneath it and swallowed their clicks. Playwright surfaced
this second one: a row click failed with "h3 intercepts pointer events".
Day headers are no longer sticky. A page holds 20 messages, so groups are
short and pinning bought little in exchange for those failures.
Footer links are left-aligned on mobile. Centred links in a single column had
no common edge to scan down.
Modal animation is now a quick fade. Dialogs slid in from the left and top
while zooming, which is the movement that read badly; the slide and zoom are
gone and the duration drops from 200ms to 150ms, applied to both the dialog
and alert-dialog primitives so every modal matches. Sheets still slide, since
that is what a drawer should do.
Guards: the overlap check runs at both mobile and desktop widths and also
asserts a row is still clickable after scrolling. Verified it fails against
the old markup rather than assuming a green test means coverage.
Fixture dates now anchor to local midnight instead of "N hours ago". A
2-hour-old message falls on the previous day when the suite runs shortly
after midnight, which made the Today/Yesterday assertions depend on the wall
clock. It failed exactly that way during this session.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Three distinct issues surfaced by React 19 / Next 16:
1. next-themes script warning + hydration mismatch. ThemeProvider lived in
the nested (app) layout, so its pre-paint <script> was re-rendered during
client navigation ("Scripts inside React components are never executed
when rendering on the client") and shifted the SSR/client tree. Moved it
to the root layout via app/theme-provider.tsx, its canonical placement.
2. Invalid <p> nesting. Radix's DialogDescription and shadcn's
CardDescription render a <p>, but call sites pass block content, giving
"<p> cannot be a descendant of <p>" / "<div> cannot be a descendant of
<p>". Fixed at the primitives: both now render a <div> (DialogDescription
uses asChild so aria-describedby wiring is preserved), which fixes every
call site at once. Also dropped a redundant wrapper in generate-api-key.
3. Nested <main>: the root layout wrapped children in <main> while the (app)
layout renders its own. Removed the outer one.
Verified by driving the app with Playwright and capturing console output
across navigation and with the API key dialog open: zero hydration, script
tag, or nesting messages (previously several). Dark mode still applies.
Build, lint (0 errors), 28 unit tests, and 16 e2e green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- promote the message-history EmptyState into components/shared/empty-state
and use it for the bare "No devices found" / "No API keys found"
placeholders (icon + title + actionable hint, fade-in)
- button primitive: subtle active:scale press state (transition covers
transform; the global prefers-reduced-motion guard still disables it)
- finish the deferred bg-gradient-to-* -> bg-linear-to-* rename (Tailwind v4
canonical utility) across the remaining four alert/modal components
Build, 25 unit tests, and 6 e2e green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Establish the testing safety net for the web dashboard before refactoring.
- Vitest + React Testing Library + jsdom for unit/component tests
- MSW node server mocks the API; unhandled requests throw so tests can
never reach a real backend
- Playwright e2e boots the real Next app but intercepts every /api/v1 call
with shared fixtures, and mints a NextAuth session cookie to run authed
- Shared fixtures in test/fixtures.ts back both layers
- Seed tests: Button unit tests, MSW sanity checks, and a dashboard e2e
happy path plus an auth-guard redirect
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* webhook notifications for sms sent, sms delivered, sms failed, and unknown state, events
* updated deliverNotification function to deliver notifications for all events
* removed sms-status-updated filter
* updated payloads based on event type
* updated payloads based on event type
---------
Co-authored-by: Israel Abebe <vernu1997@gmail.com>
* created the webhook notification history page and backend end point
* updated implementation and resolved comments
* updated sms-modal
* updated sms-modal