Add About EHSAN dropdown nav + About page
Task #15: Make «عن إحسان» top-nav a real dropdown and build the About page. - Header.tsx: converted static «عن إحسان» span into a dropdown trigger replicating the existing services dropdown pattern (aboutOpen state, trigger/panel refs, outside-click + Escape handling). Desktop panel and mobile nav both list «من نحن» (/about) and «اللجان» (/about/committees). - New pages/about.tsx: green intro panel («نبذة عن إحسان») + tab strip (الرؤية والرسالة / الأهداف والركائز / المزايا والمجالات) for the Who-we-are view, and a committee card grid for the Committees view. Section chosen via /about/:section? route param; in-page toggle between the two. Reuses Reveal for scroll-in. - App.tsx: registered <Route path="/about/:section?" component={About} />. - translations.ts: added full bilingual `about` section (AR + EN). Fully bilingual via existing t.* system, RTL verified, no emojis. tsc passes; screenshots confirm both views render correctly.
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
import { useState } from "react";
|
||||
import { useParams, Link } from "wouter";
|
||||
import { useLanguage } from "../contexts/LanguageContext";
|
||||
import { Reveal } from "../components/Reveal";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Users, Info, Check, Building2 } from "lucide-react";
|
||||
|
||||
type TabKey = "visionMission" | "goalsPillars" | "advantagesDomains";
|
||||
|
||||
function ListBlock({ title, items }: { title: string; items: string[] }) {
|
||||
return (
|
||||
<div>
|
||||
<h3 className="text-lg font-bold text-primary mb-4">{title}</h3>
|
||||
<ul className="space-y-3">
|
||||
{items.map((item, i) => (
|
||||
<li key={i} className="flex items-start gap-3">
|
||||
<Check className="w-5 h-5 text-primary shrink-0 mt-0.5" />
|
||||
<span className="text-foreground/90 leading-relaxed">{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function StatementBlock({ title, text }: { title: string; text: string }) {
|
||||
return (
|
||||
<div>
|
||||
<h3 className="text-lg font-bold text-primary mb-3">{title}</h3>
|
||||
<p className="text-foreground/90 leading-loose">{text}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function About() {
|
||||
const { t } = useLanguage();
|
||||
const params = useParams<{ section?: string }>();
|
||||
const isCommittees = params.section === "committees";
|
||||
const [tab, setTab] = useState<TabKey>("visionMission");
|
||||
|
||||
const tabs: { key: TabKey; label: string }[] = [
|
||||
{ key: "visionMission", label: t.about.tabVisionMission },
|
||||
{ key: "goalsPillars", label: t.about.tabGoalsPillars },
|
||||
{ key: "advantagesDomains", label: t.about.tabAdvantagesDomains },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-10">
|
||||
{/* Section toggle: Who we are / Committees */}
|
||||
<div className="flex justify-end mb-8">
|
||||
<div className="inline-flex rounded-full border border-border bg-muted/40 p-1">
|
||||
<Link
|
||||
href="/about"
|
||||
className={`inline-flex items-center gap-2 rounded-full px-5 py-2 text-sm font-medium transition-colors ${
|
||||
!isCommittees
|
||||
? "bg-primary/10 text-primary"
|
||||
: "text-muted-foreground hover:text-primary"
|
||||
}`}
|
||||
data-testid="about-tab-whoWeAre"
|
||||
>
|
||||
<Info className="w-4 h-4" />
|
||||
{t.about.whoWeAre}
|
||||
</Link>
|
||||
<Link
|
||||
href="/about/committees"
|
||||
className={`inline-flex items-center gap-2 rounded-full px-5 py-2 text-sm font-medium transition-colors ${
|
||||
isCommittees
|
||||
? "bg-primary/10 text-primary"
|
||||
: "text-muted-foreground hover:text-primary"
|
||||
}`}
|
||||
data-testid="about-tab-committees"
|
||||
>
|
||||
<Users className="w-4 h-4" />
|
||||
{t.about.committees}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isCommittees ? (
|
||||
<Reveal>
|
||||
<h1 className="text-2xl font-bold text-foreground mb-3">{t.about.committeesTitle}</h1>
|
||||
<p className="text-muted-foreground mb-8 max-w-3xl leading-relaxed">
|
||||
{t.about.committeesIntro}
|
||||
</p>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{t.about.committeeItems.map((c, i) => (
|
||||
<Reveal key={i} delay={(i % 2) * 0.08} className="h-full">
|
||||
<Card className="h-full">
|
||||
<CardContent className="p-6 flex items-start gap-4">
|
||||
<div className="w-11 h-11 rounded-xl bg-primary/10 text-primary flex items-center justify-center shrink-0">
|
||||
<Building2 className="w-5 h-5" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-base font-bold text-foreground mb-1">{c.name}</h3>
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">{c.desc}</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Reveal>
|
||||
))}
|
||||
</div>
|
||||
</Reveal>
|
||||
) : (
|
||||
<>
|
||||
{/* Intro green panel */}
|
||||
<Reveal>
|
||||
<section className="rounded-2xl bg-primary text-primary-foreground p-8 md:p-12 mb-10">
|
||||
<h1 className="text-2xl md:text-3xl font-bold mb-5">{t.about.introTitle}</h1>
|
||||
<p className="leading-loose text-primary-foreground/90 max-w-4xl">
|
||||
{t.about.intro}
|
||||
</p>
|
||||
</section>
|
||||
</Reveal>
|
||||
|
||||
{/* Tabs */}
|
||||
<Reveal>
|
||||
<div className="border-b border-border mb-8">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{tabs.map((tb) => (
|
||||
<button
|
||||
key={tb.key}
|
||||
type="button"
|
||||
onClick={() => setTab(tb.key)}
|
||||
data-testid={`about-section-${tb.key}`}
|
||||
className={`px-4 py-3 text-sm font-medium border-b-2 -mb-px transition-colors ${
|
||||
tab === tb.key
|
||||
? "border-primary text-primary"
|
||||
: "border-transparent text-muted-foreground hover:text-primary"
|
||||
}`}
|
||||
>
|
||||
{tb.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Reveal>
|
||||
|
||||
{/* Tab content */}
|
||||
<Reveal>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-12">
|
||||
{tab === "visionMission" && (
|
||||
<>
|
||||
<StatementBlock title={t.about.visionTitle} text={t.about.visionText} />
|
||||
<StatementBlock title={t.about.missionTitle} text={t.about.missionText} />
|
||||
</>
|
||||
)}
|
||||
{tab === "goalsPillars" && (
|
||||
<>
|
||||
<ListBlock title={t.about.goalsTitle} items={t.about.goals} />
|
||||
<ListBlock title={t.about.pillarsTitle} items={t.about.pillars} />
|
||||
</>
|
||||
)}
|
||||
{tab === "advantagesDomains" && (
|
||||
<>
|
||||
<ListBlock title={t.about.advantagesTitle} items={t.about.advantages} />
|
||||
<ListBlock title={t.about.domainsTitle} items={t.about.domains} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Reveal>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user