Skip to content

Commit 35ee7dc

Browse files
feat: move team blocked banner from sidebar to bottom status bar (#304)
## Summary Moves the team blockage banner out of the sidebar and into the dashboard bottom status bar, matching the Figma design shared by Vojtech. **Changes:** - Removed `TeamBlockageAlert` from `sidebar/footer.tsx` - Added team suspended indicator to `layouts/footer.tsx` (the bottom status bar) - Shows `⊘` BlockIcon with error-highlight color when `team.isBlocked` is true - Preserves routing logic from the old banner: - Billing-limit blocks → links to `/limits` with "Settle outstanding payment." - Default (overdue payment) blocks → links to `/billing` with "Pay now." - Uses `prose-label` styling from the theme, uppercase text - Always visible in the status bar (not hidden when sidebar collapses) - Mobile-safe: constrained width with truncation to prevent overflow ## Review & Testing Checklist for Human - [ ] Verify the blocked banner no longer appears in the sidebar - [ ] Verify the status bar shows the suspended message with correct styling (red text, ⊘ icon) when a team is blocked - [ ] Verify clicking the CTA link navigates to the correct page (billing vs limits depending on block reason) - [ ] Verify the status bar looks correct on both desktop and mobile viewports (no horizontal overflow) ### Notes - The `blocked-banner.tsx` file is left in place (no longer imported) — can be cleaned up in a follow-up if desired Link to Devin session: https://app.devin.ai/sessions/67f48c6c00574f9ebc1a292c9311bd5e --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: ben@e2b.dev <ben@e2b.dev>
1 parent 75caad0 commit 35ee7dc

3 files changed

Lines changed: 75 additions & 4 deletions

File tree

src/features/dashboard/layouts/footer.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { usePathname } from 'next/navigation'
44
import { getDashboardLayoutConfig } from '@/configs/layout'
55
import { useDashboard } from '@/features/dashboard/context'
6+
import TeamBlockedIndicator from './team-blocked-indicator'
67

78
interface DashboardLayoutFooterProps {
89
statusBanner: React.ReactNode
@@ -20,14 +21,18 @@ export default function DashboardLayoutFooter({
2021
: config.title.map((segment) => segment.label).join('/')
2122

2223
return (
23-
<footer className="flex h-protected-footer min-h-protected-footer shrink-0 items-center justify-between gap-2 border-t bg-bg px-3 md:px-6">
24+
<footer className="flex h-protected-footer min-h-protected-footer shrink-0 items-center gap-2 border-t bg-bg px-3 md:px-6">
2425
<span className="min-w-0 flex-1 truncate pr-2 font-mono text-xs text-fg-tertiary uppercase md:pr-4 md:prose-label">
2526
{'>_'}
2627
{user.email ?? 'ANONYMOUS@UNKNOWN.COM'}
2728
{`:${footerTitle}`}
2829
</span>
2930

30-
{statusBanner}
31+
<TeamBlockedIndicator />
32+
33+
{statusBanner ? (
34+
<div className="flex flex-1 justify-end">{statusBanner}</div>
35+
) : null}
3136
</footer>
3237
)
3338
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
'use client'
2+
3+
import Link from 'next/link'
4+
import { useMemo } from 'react'
5+
import { PROTECTED_URLS } from '@/configs/urls'
6+
import { useDashboard } from '@/features/dashboard/context'
7+
import { BlockIcon } from '@/ui/primitives/icons'
8+
9+
function useBlockedMessage(slug: string, blockedReason: string | null) {
10+
return useMemo(() => {
11+
const reason = blockedReason?.toLowerCase() ?? ''
12+
13+
if (reason.includes('billing limit')) {
14+
return {
15+
text: 'Billing limit reached.',
16+
cta: 'Update limit.',
17+
href: PROTECTED_URLS.LIMITS(slug),
18+
}
19+
}
20+
21+
if (reason.includes('missing payment method')) {
22+
return {
23+
text: 'Missing payment method.',
24+
cta: 'Add payment method.',
25+
href: PROTECTED_URLS.BILLING(slug),
26+
}
27+
}
28+
29+
if (reason.includes('verification required')) {
30+
return {
31+
text: 'Verification required.',
32+
cta: 'Add payment method.',
33+
href: PROTECTED_URLS.BILLING(slug),
34+
}
35+
}
36+
37+
return {
38+
text: blockedReason ?? 'Team suspended.',
39+
cta: null,
40+
href: null,
41+
}
42+
}, [slug, blockedReason])
43+
}
44+
45+
export default function TeamBlockedIndicator() {
46+
const { team } = useDashboard()
47+
48+
const message = useBlockedMessage(team.slug, team.blockedReason)
49+
50+
if (!team.isBlocked) return null
51+
52+
return (
53+
<div className="inline-flex shrink-0 items-center gap-1.5 text-accent-error-highlight max-md:max-w-[50%]">
54+
<BlockIcon className="size-4 shrink-0" />
55+
<span className="truncate text-xs uppercase md:prose-label">
56+
{message.text}
57+
{message.cta && message.href && (
58+
<>
59+
{' '}
60+
<Link href={message.href} className="underline">
61+
{message.cta}
62+
</Link>
63+
</>
64+
)}
65+
</span>
66+
</div>
67+
)
68+
}

src/features/dashboard/sidebar/footer.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,13 @@ import {
2424
} from '@/ui/primitives/sidebar'
2525
import DashboardSurveyPopover from '../navbar/dashboard-survey-popover'
2626
import ContactSupportDialog from '../navbar/report-issue-dialog'
27-
import TeamBlockageAlert from './blocked-banner'
2827

2928
export default function DashboardSidebarFooter() {
3029
return (
3130
<>
3231
<SidebarFooter>
3332
<SidebarGroup className="!p-0">
3433
<SidebarMenu>
35-
<TeamBlockageAlert className="mb-2" />
3634
<SidebarMenuItem key="github">
3735
<SidebarMenuButton asChild tooltip="GitHub">
3836
<Link

0 commit comments

Comments
 (0)