Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 21 additions & 15 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,10 @@ Technology stack:
- `/admin/dashboard`, `/admin/upload` - Admin interfaces

**Layout** (`app/layout.tsx`):
- Theme provider (dark mode via next-themes)
- Site header with navigation (site-header.tsx)
- Footer, toast notifications, Vercel Analytics, Google Analytics
- Owns the only document/app shell: `<html>`, `<body>`, theme provider, analytics, toast notifications, and floating chat bubble
- Renders `LayoutContent`, which owns the site header, scrollable page wrapper, and footer visibility
- Chat routes hide the footer for a full-screen chat experience
- Locale routes under `app/[locale]/layout.tsx` are nested providers only; they must not render another `<html>`, `<body>`, header, footer, analytics, or theme provider

**Navigation** (`config/site.ts`): Home, Chat, About, Proceedings, Acts, Constitution

Expand Down Expand Up @@ -372,20 +373,25 @@ These client components wrap server components to trigger GA events on mount.
- Smooth scrolling when at bottom

**Benefits**:
- Users can read previous messages without interruption
- Auto-scroll still works when at bottom
- Much better mobile experience
- No more jumping/jarring scrolls during message streaming
- ✅ Users can read previous messages without interruption
- ✅ Auto-scroll still works when at bottom
- ✅ Much better mobile experience
- ✅ No more jumping/jarring scrolls during message streaming

### 2. Duplicate Navigation Bar Fix
**Issue**: Chat page showed duplicate elements at top and bottom - main navigation bar at top, and footer appearing at bottom below chat area, cluttering the interface.
**Issue**: Chat pages showed duplicate page chrome: a main navigation bar at the top and another navigation/footer region lower on the page. On locale routes such as `/en/chat`, the app could feel like two pages were rendered inside one page.

**Solution**: Created `LayoutContent` component to conditionally hide footer on chat pages while keeping the main navigation. The footer itself already contains only the attribution text (no duplicate nav items), but it appeared as clutter in the chat interface.
**Root cause**: `app/[locale]/layout.tsx` was rendering a second full app shell inside the root shell, including `<html>`, `<body>`, `ThemeProvider`, `SiteHeader`, `Footer`, analytics, toast notifications, and floating chat. React also hit a hydration mismatch because Urdu line-height CSS was rendered as inline `<style>` text inside the locale provider.

**Solution**: Keep the document shell in `app/layout.tsx` only. `app/[locale]/layout.tsx` now provides locale context and locale attributes only. `LayoutContent` handles the single header, scroll wrapper, and footer hiding for all chat routes, including locale routes. Urdu typography rules live in `styles/globals.css` instead of an inline style tag.

**Files Modified**:
- `app/layout.tsx` - Uses new `LayoutContent` component instead of directly rendering SiteHeader/Footer
- `app/[locale]/layout.tsx` - Nested locale provider only; no duplicate document/app shell
- `components/layout-content.tsx` - New component that conditionally renders navigation based on route
- `components/footer.tsx` - Already optimized (shows only attribution: "Built with ❤️ by Code For Pakistan")
- `components/footer.tsx` - Hides on any pathname containing `/chat`
- `components/floating-chat-bubble.tsx` - Fixed malformed smart-scroll code and hides on localized chat routes
- `styles/globals.css` - Contains Urdu line-height rules to avoid hydration mismatch

**How it works**:
```typescript
Expand All @@ -395,16 +401,16 @@ return (
<>
<SiteHeader /> // Always shown
<div>{children}</div>
{!isChatPage && <Footer />} // Hidden only on chat pages
{!isChatPage && <Footer />} // Hidden on /chat, /en/chat, /ur/chat, etc.
</>
)
```

**Benefits**:
- Clean chat interface with only top navigation
- No unnecessary footer clutter on chat page
- ✅ Footer attribution still shows on all other pages
- ✅ Focused, distraction-free chat experience
- Clean chat interface with only one top navigation
- No duplicate footer/navigation region on localized routes
- No nested `<html>`/`<body>` or duplicate providers
- No inline Urdu style hydration mismatch

## External Services

Expand Down
45 changes: 9 additions & 36 deletions app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
import "@/styles/globals.css"
import { Metadata } from "next"
import { notFound } from "next/navigation"
import { NextIntlClientProvider } from "next-intl"
import { getMessages, getTranslations } from "next-intl/server"

import { routing, Locale } from "@/i18n/routing"
import { fontSans, fontUrdu } from "@/lib/fonts"
import { fontUrdu } from "@/lib/fonts"
import { cn } from "@/lib/utils"
import { SiteHeader } from "@/components/site-header"
import { TailwindIndicator } from "@/components/tailwind-indicator"
import { ThemeProvider } from "@/components/theme-provider"
import { Footer } from "@/components/footer"
import { Toaster } from "@/components/ui/toaster"
import { Analytics } from "@vercel/analytics/react"
import { GoogleAnalytics } from "@/components/google-analytics"
import { FloatingChatBubble } from "@/components/floating-chat-bubble"

export async function generateMetadata({
params: { locale },
Expand Down Expand Up @@ -67,37 +58,19 @@ export default async function LocaleLayout({ children, params: { locale } }: Loc
const dir = isUrdu ? "rtl" : "ltr"

return (
<html lang={locale} dir={dir} suppressHydrationWarning>
<head>
<GoogleAnalytics />
</head>
<body
<NextIntlClientProvider locale={locale} messages={messages}>
<div
lang={locale}
dir={dir}
className={cn(
"min-h-screen bg-background antialiased",
"flex min-h-0 flex-1 flex-col",
isUrdu ? "font-urdu" : "font-sans",
fontSans.variable,
fontUrdu.variable,
isUrdu && "leading-relaxed"
)}
>
<style>{`
[lang="ur"] { line-height: 2; }
[lang="ur"] h1, [lang="ur"] h2, [lang="ur"] h3 { line-height: 1.6; }
`}</style>
<NextIntlClientProvider locale={locale} messages={messages}>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<div className="relative flex h-screen flex-col">
<SiteHeader />
<div className="flex min-h-0 flex-1 overflow-y-auto">{children}</div>
<Footer />
<Toaster />
<FloatingChatBubble />
</div>
<TailwindIndicator />
</ThemeProvider>
</NextIntlClientProvider>
<Analytics />
</body>
</html>
{children}
</div>
</NextIntlClientProvider>
)
}
2 changes: 0 additions & 2 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ import { Metadata } from "next"
import { siteConfig } from "@/config/site"
import { fontSans } from "@/lib/fonts"
import { cn } from "@/lib/utils"
import { SiteHeader } from "@/components/site-header"
import { TailwindIndicator } from "@/components/tailwind-indicator"
import { ThemeProvider } from "@/components/theme-provider"
import { Footer } from "@/components/footer"
import { Toaster } from "@/components/ui/toaster"
import { Analytics } from "@vercel/analytics/react"
import { GoogleAnalytics } from "@/components/google-analytics"
Expand Down
15 changes: 6 additions & 9 deletions components/floating-chat-bubble.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,14 @@ export function FloatingChatBubble() {
"Hello! I am Numainda, your guide to Pakistan's constitutional and electoral information. How may I assist you today?",
},
],
onResponse: (response) => {
if// Don't auto-scroll here; let the useEffect handle it
setIsGenerating(false)
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" })
}
onResponse: () => {
setIsGenerating(false)
},
onError: (error) => {
if (error) setIsGenerating(false)
},
})

// Track if user is at bottom of messages
const handleScroll = useCallback(() => {
if (!messagesContainerRef.current) return
Expand All @@ -84,12 +83,10 @@ export function FloatingChatBubble() {
if (isOpen) {
shouldAutoScrollRef.current = true
}
}, [dRef.current?.scrollIntoView({ behavior: "smooth" })
}
}, [messages, isOpen])
}, [isOpen])

// Hide on homepage and chat page (they have their own chat UI)
if (pathname === "/" || pathname === "/chat") {
if (pathname === "/" || pathname.includes("/chat")) {
return null
}

Expand Down
4 changes: 2 additions & 2 deletions components/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { cn } from "@/lib/utils"
export function Footer({ className }: { className?: string }) {
const pathname = usePathname()

// Hide footer on the chat page (full-screen chat experience)
if (pathname === "/chat") {
// Hide footer on chat pages (full-screen chat experience)
if (pathname.includes("/chat")) {
return null
}

Expand Down
10 changes: 10 additions & 0 deletions styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,14 @@
@apply bg-background text-foreground;
font-feature-settings: "rlig" 1, "calt" 1;
}

[lang="ur"] {
line-height: 2;
}

[lang="ur"] h1,
[lang="ur"] h2,
[lang="ur"] h3 {
line-height: 1.6;
}
}
Loading