Fix nav active state & populate services menu

Task #9: Two header fixes on artifacts/ehsan-poc.

1. Nav active state no longer stuck on "فرص التبرع". Active page now gets
   a moving green outline box (Home) matching the real ehsan.sa nav.
   "فرص التبرع" stays a solid green CTA but only shows an active green ring
   when on /opportunities. Applied consistent active styling across desktop
   and mobile nav (Home, Opportunities, Request).

2. "خدماتنا" dropdown now lists the eight official services from the
   reference image, each with a lucide icon: غراس (Sprout), الزكاة
   (HandCoins), هدية (Gift), الأضاحي (Beef), الحملات (Megaphone), التبرع
   الدوري (Repeat), التبرع بالرسائل (MessageSquare), تطهير الأسهم
   (TrendingUp). Each routes to /opportunities (POC has no per-service
   pages). Kept "طلب دعم" below a separator so desktop users retain access
   to /request. Mobile menu mirrors the services list.

Added serviceItems i18n group (AR + EN) in translations.ts.

Files: src/components/layout/Header.tsx, src/lib/i18n/translations.ts.
Verified: tsc --noEmit clean; e2e UI test passed (active state moves,
dropdown shows all 8 items + request, routing works); console clean after
HMR-restart of the web workflow. No emojis.
This commit is contained in:
Replit Agent
2026-06-05 18:33:23 +00:00
parent 100dbfe342
commit 61e66824b9
2 changed files with 92 additions and 9 deletions
@@ -7,6 +7,7 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "../ui/dropdown-menu";
import {
@@ -18,6 +19,15 @@ import {
X,
LogOut,
Globe,
Sprout,
HandCoins,
Gift,
Beef,
Megaphone,
Repeat,
MessageSquare,
TrendingUp,
HeartHandshake,
} from "lucide-react";
import ehsanLogo from "../../assets/ehsan-logo.png";
@@ -31,6 +41,17 @@ export function Header() {
const isActive = (path: string) => location === path;
const services = [
{ key: "ghiras", label: t.serviceItems.ghiras, icon: Sprout },
{ key: "zakat", label: t.serviceItems.zakat, icon: HandCoins },
{ key: "gift", label: t.serviceItems.gift, icon: Gift },
{ key: "adahi", label: t.serviceItems.adahi, icon: Beef },
{ key: "campaigns", label: t.serviceItems.campaigns, icon: Megaphone },
{ key: "recurring", label: t.serviceItems.recurring, icon: Repeat },
{ key: "sms", label: t.serviceItems.sms, icon: MessageSquare },
{ key: "stocks", label: t.serviceItems.stocks, icon: TrendingUp },
];
return (
<header className="bg-white sticky top-0 z-50 shadow-sm">
<div className="container mx-auto px-4 h-16 flex items-center justify-between gap-4">
@@ -43,8 +64,10 @@ export function Header() {
<nav className="hidden lg:flex items-center gap-1 flex-1 justify-center">
<Link
href="/"
className={`px-3 py-2 text-sm font-medium rounded-md transition-colors ${
isActive("/") ? "text-primary" : "text-foreground hover:text-primary"
className={`px-3 py-2 text-sm font-medium rounded-md border transition-colors ${
isActive("/")
? "border-primary text-primary"
: "border-transparent text-foreground hover:text-primary"
}`}
data-testid="nav-home"
>
@@ -57,7 +80,9 @@ export function Header() {
<Link
href="/opportunities"
className="px-4 py-2 text-sm font-bold rounded-md bg-primary text-primary-foreground inline-flex items-center gap-1 hover:opacity-90 transition-opacity"
className={`px-4 py-2 text-sm font-bold rounded-md bg-primary text-primary-foreground inline-flex items-center gap-1 hover:opacity-90 transition-opacity ${
isActive("/opportunities") ? "ring-2 ring-primary ring-offset-2" : ""
}`}
data-testid="nav-opportunities"
>
{t.nav.opportunities}
@@ -74,9 +99,26 @@ export function Header() {
<ChevronDown className="w-3.5 h-3.5" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="center">
<DropdownMenuItem onClick={() => setLocation("/request")} data-testid="nav-requestSupport">
{t.nav.requestSupport}
<DropdownMenuContent align="center" className="w-56">
{services.map((s) => (
<DropdownMenuItem
key={s.key}
onClick={() => setLocation("/opportunities")}
data-testid={`nav-service-${s.key}`}
className="gap-2 cursor-pointer"
>
<s.icon className="w-4 h-4 text-primary" />
<span>{s.label}</span>
</DropdownMenuItem>
))}
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={() => setLocation("/request")}
data-testid="nav-requestSupport"
className="gap-2 cursor-pointer"
>
<HeartHandshake className="w-4 h-4 text-primary" />
<span>{t.nav.requestSupport}</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
@@ -164,21 +206,42 @@ export function Header() {
<Link
href="/"
onClick={() => setMobileOpen(false)}
className="block px-3 py-2 rounded-md text-sm font-medium text-foreground hover:bg-muted"
className={`block px-3 py-2 rounded-md text-sm font-medium hover:bg-muted ${
isActive("/") ? "text-primary bg-muted" : "text-foreground"
}`}
>
{t.nav.home}
</Link>
<Link
href="/opportunities"
onClick={() => setMobileOpen(false)}
className="block px-3 py-2 rounded-md text-sm font-bold text-primary hover:bg-muted"
className={`block px-3 py-2 rounded-md text-sm font-bold hover:bg-muted ${
isActive("/opportunities") ? "bg-primary text-primary-foreground" : "text-primary"
}`}
>
{t.nav.opportunities}
</Link>
<div className="pt-1">
<p className="px-3 py-1 text-xs font-semibold text-foreground/50">
{t.nav.services}
</p>
{services.map((s) => (
<button
key={s.key}
onClick={() => { setMobileOpen(false); setLocation("/opportunities"); }}
className="flex w-full items-center gap-2 text-start px-3 py-2 rounded-md text-sm font-medium text-foreground hover:bg-muted"
>
<s.icon className="w-4 h-4 text-primary" />
<span>{s.label}</span>
</button>
))}
</div>
<Link
href="/request"
onClick={() => setMobileOpen(false)}
className="block px-3 py-2 rounded-md text-sm font-medium text-foreground hover:bg-muted"
className={`block px-3 py-2 rounded-md text-sm font-medium hover:bg-muted ${
isActive("/request") ? "text-primary bg-muted" : "text-foreground"
}`}
>
{t.nav.requestSupport}
</Link>