mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-13 21:01:21 +08:00
fix(dashboard): resolve UI issues across email-* pages (#1345)
## Summary
Six UI issues found across the email-* dashboard pages, ranked by
impact, fixed here:
1. **email-sent layout** — the email log table and domain reputation
card were forced side-by-side at all widths. A fixed-width sidebar plus
a flex-1 table meant that on tablet the table got crushed, and on mobile
the row overflowed horizontally. Fix: stack vertically below `lg`, and
let the reputation card span full width on narrow viewports.
2. **Domain status enum leaks to the UI** — `<span>Status:
{domain.status}</span>` rendered raw values like `pending_dns` /
`pending_verification`. Added a `MANAGED_DOMAIN_STATUS_LABELS` map and
route through it before rendering.
3. **email-themes dialog grid cramped on mobile** — the Change Theme
dialog hardcoded `grid-cols-2`, so at 375px each theme card had ~150px
and the preview images were illegible. Changed to `grid-cols-1
sm:grid-cols-2`.
4. **Template name row overflow** — long template names pushed the Edit
Template button off the right edge of the card because the flex row had
no `min-w-0` / `truncate`. Fixed both, and made the action column
`shrink-0`.
5. **Boosted-capacity label was color-only** — during an active boost
the label used a red strikethrough for the base value and a blue number
for the boosted value with no non-color cue. Added an explicit `→` arrow
between the two numbers, `title` tooltips on each, and a visible
\"(boosted)\" marker after `/h max`.
6. **Draft progress bar overflowed at mobile width** — the 4-step
progress bar used fixed 80px connectors, giving a minimum width of
~400px that clipped off both ends at 375px. Changed connectors to `w-8
sm:w-20` (32px on mobile, 80px otherwise) so all four steps and their
labels fit below 640px.
## Before / after
Each GIF below loops \"before\" (1s) → \"after\" (1s) with a red pill in
the top-right indicating which frame is which. Full-size stills (before
+ after + extra viewports) are listed under **All screenshots** at the
bottom.
### 1. email-sent — two-column layout collapses on narrow viewports
Mobile (375px):

Tablet (900px):

### 2. email-settings — managed-domain status label

### 3. email-themes — Change Theme dialog on mobile

### 4. email-templates — long name overflow

### 5. email-sent — boosted capacity label

### 7. email-drafts — draft progress bar on mobile

## Test plan
- [x] \`pnpm --filter @stackframe/dashboard lint\` — clean
- [x] \`pnpm --filter @stackframe/dashboard typecheck\` — clean
- [x] Manual verification in a browser at 375px / 900px / 1440px, light
+ dark mode, for each fixed page
- [ ] Reviewer sanity check of the remaining email-* pages
(email-outbox, email-viewer) for similar responsive regressions
## Notes
- The initial review flagged a \"white-on-white capacity boost timer\" —
on closer look the label sits on a deliberately dark `bg-zinc-900/0.82`
overlay inside the boost card, so it reads fine in light and dark mode.
Not fixing; that part of the review was a false positive.
- The initial review also flagged a missing empty state on
email-templates. Because Stack seeds built-in templates, the empty
branch is unreachable in practice — skipping that fix to avoid dead
code.
## All screenshots
Gist with all the individual before/after PNGs and the GIFs themselves:
https://gist.github.com/BilalG1/edb04740a19c3f2d048da6e602209d45
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Release Notes
* **New Features**
* Added human-readable status labels for managed domains in domain
settings
* **Improvements**
* Enhanced responsive layouts across dashboard pages for improved mobile
experience
* Improved email capacity display with visual indicators and tooltips
for boost status
* Refined template and theme selection layouts with better text handling
and spacing
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
85ae4b1c9e
commit
6bc1836e66
@ -55,7 +55,7 @@ export function DraftProgressBar({ steps, currentStep, onStepClick, disableNavig
|
||||
</button>
|
||||
|
||||
{!isLast && (
|
||||
<div className="w-20 h-1 bg-muted-foreground/15 overflow-hidden">
|
||||
<div className="w-8 sm:w-20 h-1 bg-muted-foreground/15 overflow-hidden">
|
||||
<div
|
||||
className={cn(
|
||||
"h-full bg-primary transition-all duration-300",
|
||||
@ -86,7 +86,7 @@ export function DraftProgressBar({ steps, currentStep, onStepClick, disableNavig
|
||||
>
|
||||
{step.label}
|
||||
</span>
|
||||
{!isLast && <div className="w-20" />}
|
||||
{!isLast && <div className="w-8 sm:w-20" />}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
@ -247,10 +247,20 @@ export function DomainReputationCard() {
|
||||
const capacityLabel = isBoostActive ? (
|
||||
<span>
|
||||
{hourlyUsed} of{" "}
|
||||
<span className="text-red-500 line-through">{Math.round(baseHourlyCapacity)}</span>
|
||||
{" "}
|
||||
<span className="text-blue-500 font-medium">{Math.round(hourlyCapacity)}</span>
|
||||
/h max
|
||||
<span
|
||||
className="text-red-500 line-through"
|
||||
title="Base hourly capacity (replaced by active boost)"
|
||||
>
|
||||
{Math.round(baseHourlyCapacity)}
|
||||
</span>
|
||||
{" \u2192 "}
|
||||
<span
|
||||
className="text-blue-500 font-medium"
|
||||
title="Boosted hourly capacity"
|
||||
>
|
||||
{Math.round(hourlyCapacity)}
|
||||
</span>
|
||||
/h max <span className="text-blue-500 font-medium">(boosted)</span>
|
||||
</span>
|
||||
) : (
|
||||
`${hourlyUsed} of ${Math.round(hourlyCapacity)}/h max`
|
||||
@ -260,7 +270,7 @@ export function DomainReputationCard() {
|
||||
<DesignCard
|
||||
gradient="default"
|
||||
glassmorphic
|
||||
className="w-72"
|
||||
className="w-full lg:w-72"
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<div className="p-1 rounded-md bg-foreground/[0.06] dark:bg-foreground/[0.04]">
|
||||
|
||||
@ -134,7 +134,7 @@ export default function PageClient() {
|
||||
title="Sent"
|
||||
description="View email logs and domain reputation"
|
||||
>
|
||||
<div data-walkthrough="emails-sent" className="flex gap-6">
|
||||
<div data-walkthrough="emails-sent" className="flex flex-col lg:flex-row gap-6">
|
||||
{/* Left side: Email Log with toggle inside card */}
|
||||
<div className="flex-1 flex flex-col gap-4">
|
||||
<DesignCard
|
||||
|
||||
@ -40,6 +40,14 @@ const SERVER_TYPE_LABELS: Record<ServerType, string> = {
|
||||
standard: "Custom SMTP",
|
||||
};
|
||||
|
||||
const MANAGED_DOMAIN_STATUS_LABELS: Record<ManagedDomainStatus, string> = {
|
||||
pending_dns: "Pending DNS records",
|
||||
pending_verification: "Pending verification",
|
||||
verified: "Verified",
|
||||
applied: "Applied",
|
||||
failed: "Failed",
|
||||
};
|
||||
|
||||
const VISIBLE_FIELDS: Record<ServerType, ServerFieldConfig[]> = {
|
||||
shared: [],
|
||||
managed: [],
|
||||
@ -317,7 +325,7 @@ function ManagedEmailSetupDialog(props: { trigger: React.ReactNode }) {
|
||||
<Alert key={domain.domainId} className="bg-slate-500/5 border-slate-500/20">
|
||||
<AlertTitle className="font-mono text-xs">{domain.senderLocalPart}@{domain.subdomain}</AlertTitle>
|
||||
<AlertDescription className="mt-1 flex items-center justify-between gap-2">
|
||||
<span className="text-xs">Status: {domain.status}</span>
|
||||
<span className="text-xs">Status: {(MANAGED_DOMAIN_STATUS_LABELS as Record<string, string>)[domain.status] ?? domain.status}</span>
|
||||
<DesignButton
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
|
||||
@ -63,17 +63,17 @@ export default function PageClient() {
|
||||
gradient="default"
|
||||
contentClassName="p-4"
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="p-2.5 rounded-xl bg-foreground/[0.04] ring-1 ring-foreground/[0.06]">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="flex items-center gap-4 min-w-0 flex-1">
|
||||
<div className="p-2.5 rounded-xl bg-foreground/[0.04] ring-1 ring-foreground/[0.06] shrink-0">
|
||||
<EnvelopeSimpleIcon className="h-5 w-5 text-muted-foreground" />
|
||||
</div>
|
||||
<Typography className="font-semibold text-foreground">
|
||||
<Typography className="font-semibold text-foreground truncate">
|
||||
{template.displayName}
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
|
||||
@ -193,7 +193,7 @@ export default function PageClient() {
|
||||
onClick: handleSaveTheme
|
||||
}}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
{themes.map((theme) => (
|
||||
<ThemeOption
|
||||
key={theme.id}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user