Make «الوقف» (Endowment) and «براعم إحسان» (EHSAN Buds) into real pages

Task #16: The last two top-nav items were static, non-clickable text.
Turned both into real bilingual (AR/EN) routed pages, completing the
main navigation of the EHSAN POC.

Changes:
- translations.ts: added parallel `waqf` and `baraem` sections to both
  `en` and `ar` (intro, contribution ways, endowment fields/features for
  waqf; goals, features, audience for baraem). Content mirrors ehsan.sa
  section structure; no emojis.
- New pages src/pages/waqf.tsx and src/pages/baraem.tsx, built on the
  about.tsx pattern: green intro panel, Reveal scroll-in, Card grids,
  RTL-correct using logical classes and the t.* system.
- App.tsx: registered /waqf and /baraem routes.
- Header.tsx: replaced the two static <span> items with active-aware
  <Link>s in desktop nav, and added matching entries to the mobile nav.

Verified: tsc --noEmit passes; both pages render correctly in Arabic
RTL with active nav highlighting (screenshots checked).

Replit-Task-Id: 40da5508-41c9-4ccd-aba4-2ea80183a97b
This commit is contained in:
riyadhafraa
2026-06-05 19:57:31 +00:00
parent d0d504bc74
commit d66ebddd00
6 changed files with 389 additions and 4 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

+4
View File
@@ -12,6 +12,8 @@ import { ComponentType } from "react";
// Page imports // Page imports
import Home from "./pages/home"; import Home from "./pages/home";
import About from "./pages/about"; import About from "./pages/about";
import Waqf from "./pages/waqf";
import Baraem from "./pages/baraem";
import RequestSupport from "./pages/request"; import RequestSupport from "./pages/request";
import Opportunities from "./pages/opportunities"; import Opportunities from "./pages/opportunities";
import Donate from "./pages/donate"; import Donate from "./pages/donate";
@@ -38,6 +40,8 @@ function Router() {
<Switch> <Switch>
<Route path="/" component={Home} /> <Route path="/" component={Home} />
<Route path="/about/:section?" component={About} /> <Route path="/about/:section?" component={About} />
<Route path="/waqf" component={Waqf} />
<Route path="/baraem" component={Baraem} />
<Route path="/request" component={RequestSupport} /> <Route path="/request" component={RequestSupport} />
<Route path="/opportunities" component={Opportunities} /> <Route path="/opportunities" component={Opportunities} />
<Route path="/donate/:id" component={Donate} /> <Route path="/donate/:id" component={Donate} />
@@ -133,9 +133,17 @@ export function Header() {
{t.nav.home} {t.nav.home}
</Link> </Link>
<span className="px-3 py-2 text-sm font-medium text-foreground/70 cursor-default select-none"> <Link
href="/waqf"
className={`px-4 py-2 text-sm font-medium rounded-md transition-colors ${
isActive("/waqf")
? "bg-primary text-primary-foreground font-bold"
: "text-foreground hover:text-primary"
}`}
data-testid="nav-waqf"
>
{t.nav.waqf} {t.nav.waqf}
</span> </Link>
<Link <Link
href="/opportunities" href="/opportunities"
@@ -188,9 +196,17 @@ export function Header() {
/> />
</button> </button>
<span className="px-3 py-2 text-sm font-medium text-foreground/70 cursor-default select-none"> <Link
href="/baraem"
className={`px-4 py-2 text-sm font-medium rounded-md transition-colors ${
isActive("/baraem")
? "bg-primary text-primary-foreground font-bold"
: "text-foreground hover:text-primary"
}`}
data-testid="nav-baraem"
>
{t.nav.baraem} {t.nav.baraem}
</span> </Link>
</nav> </nav>
{/* Utility icons */} {/* Utility icons */}
@@ -359,6 +375,17 @@ export function Header() {
> >
{t.nav.home} {t.nav.home}
</Link> </Link>
<Link
href="/waqf"
onClick={() => setMobileOpen(false)}
className={`block px-3 py-2 rounded-md text-sm hover:bg-muted ${
isActive("/waqf")
? "bg-primary text-primary-foreground font-bold"
: "text-foreground font-medium"
}`}
>
{t.nav.waqf}
</Link>
<Link <Link
href="/opportunities" href="/opportunities"
onClick={() => setMobileOpen(false)} onClick={() => setMobileOpen(false)}
@@ -415,6 +442,17 @@ export function Header() {
<span>{t.about.committees}</span> <span>{t.about.committees}</span>
</Link> </Link>
</div> </div>
<Link
href="/baraem"
onClick={() => setMobileOpen(false)}
className={`block px-3 py-2 rounded-md text-sm hover:bg-muted ${
isActive("/baraem")
? "bg-primary text-primary-foreground font-bold"
: "text-foreground font-medium"
}`}
>
{t.nav.baraem}
</Link>
<div className="pt-2 mt-2 border-t"> <div className="pt-2 mt-2 border-t">
{isAuthenticated ? ( {isAuthenticated ? (
<> <>
@@ -110,6 +110,102 @@ export const en = {
}, },
], ],
}, },
waqf: {
pageTitle: "Endowment",
introTitle: "EHSAN Endowment",
intro:
"The endowment program enables you to build a sustainable charitable legacy whose reward continues across generations. Through EHSAN you can establish a new endowment or contribute to existing endowments that fund developmental and humanitarian projects with lasting impact.",
waysTitle: "Ways to Contribute",
ways: [
{
name: "Establish an Endowment",
desc: "Create your own endowment and direct its returns to the causes closest to your heart.",
},
{
name: "Contribute to an Endowment",
desc: "Add your share to an existing endowment and multiply its sustainable impact.",
},
{
name: "Endowment Shares",
desc: "Buy one or more endowment shares with a value that suits you.",
},
{
name: "Corporate Endowments",
desc: "Enable institutions to build endowment portfolios that serve their social responsibility.",
},
],
fieldsTitle: "Endowment Fields",
fields: [
"Education and knowledge.",
"Healthcare.",
"Care for orphans and families.",
"Mosques and the Holy Quran.",
"Water and the environment.",
"Relief and humanitarian aid.",
],
featuresTitle: "Why an Endowment",
features: [
{
name: "Sustainable Reward",
desc: "Continuous charity whose reward flows even beyond one's lifetime.",
},
{
name: "Governed and Trusted",
desc: "Endowments are managed transparently and in line with Sharia governance.",
},
{
name: "Measurable Impact",
desc: "Follow how your endowment returns are deployed into real projects.",
},
],
},
baraem: {
pageTitle: "EHSAN Buds",
introTitle: "EHSAN Buds",
intro:
"EHSAN Buds is an initiative dedicated to nurturing the value of giving in children and instilling a love of charitable work from an early age, through age-appropriate experiences that turn generosity into a lasting habit.",
goalsTitle: "Program Goals",
goals: [
"Instill the value of giving in the next generation.",
"Connect children with the meaning of social responsibility.",
"Offer a safe, guided giving experience for families.",
"Build sustained charitable behavior from an early age.",
],
featuresTitle: "What Buds Offers",
features: [
{
name: "Child-Friendly Giving",
desc: "Simple donation journeys designed for young hearts and minds.",
},
{
name: "Family Together",
desc: "Parents and children give side by side and share the reward.",
},
{
name: "Educational Content",
desc: "Stories and activities that explain the value of charity.",
},
{
name: "Impact for Children",
desc: "Causes focused on children, orphans, and education.",
},
],
audienceTitle: "Who It's For",
audience: [
{
name: "Early Childhood",
desc: "Gentle introductions to kindness and sharing.",
},
{
name: "School Age",
desc: "Guided giving tied to real, understandable causes.",
},
{
name: "Families",
desc: "A shared space for parents and children to give together.",
},
],
},
serviceItems: { serviceItems: {
ghiras: "Ghiras", ghiras: "Ghiras",
zakat: "Zakat", zakat: "Zakat",
@@ -412,6 +508,102 @@ export const ar = {
}, },
], ],
}, },
waqf: {
pageTitle: "الوقف",
introTitle: "وقف إحسان",
intro:
"يمكّنك برنامج الوقف من بناء أثر خيري مستدام يستمر أجره عبر الأجيال. ومن خلال إحسان يمكنك إنشاء وقف جديد أو المساهمة في أوقاف قائمة تموّل مشاريع تنموية وإنسانية ذات أثر دائم.",
waysTitle: "أوجه المساهمة",
ways: [
{
name: "إنشاء وقف",
desc: "أنشئ وقفك الخاص ووجّه ريعه إلى الأبواب الأقرب إلى قلبك.",
},
{
name: "المساهمة في وقف",
desc: "أضف نصيبك إلى وقف قائم وضاعف أثره المستدام.",
},
{
name: "أسهم وقفية",
desc: "اشترِ سهماً وقفياً أو أكثر بقيمة تناسبك.",
},
{
name: "الأوقاف المؤسسية",
desc: "تمكين المؤسسات من بناء محافظ وقفية تخدم مسؤوليتها الاجتماعية.",
},
],
fieldsTitle: "مجالات الوقف",
fields: [
"التعليم والمعرفة.",
"الرعاية الصحية.",
"رعاية الأيتام والأسر.",
"المساجد والقرآن الكريم.",
"السقيا والبيئة.",
"الإغاثة والمساعدات الإنسانية.",
],
featuresTitle: "لماذا الوقف",
features: [
{
name: "أجر مستدام",
desc: "صدقة جارية يستمر أجرها حتى بعد الممات.",
},
{
name: "موثوق ومحوكم",
desc: "تُدار الأوقاف بشفافية ووفق الحوكمة الشرعية.",
},
{
name: "أثر قابل للقياس",
desc: "تابع كيف يُوظّف ريع وقفك في مشاريع حقيقية.",
},
],
},
baraem: {
pageTitle: "براعم إحسان",
introTitle: "براعم إحسان",
intro:
"براعم إحسان مبادرة مخصصة لغرس قيمة العطاء في نفوس الأطفال وتنمية حب العمل الخيري منذ الصغر، عبر تجارب تناسب أعمارهم تحوّل الجود إلى عادة راسخة.",
goalsTitle: "أهداف البرنامج",
goals: [
"غرس قيمة العطاء في الجيل القادم.",
"ربط الأطفال بمعنى المسؤولية الاجتماعية.",
"تقديم تجربة تبرع آمنة وموجهة للأسر.",
"بناء سلوك خيري مستدام منذ الصغر.",
],
featuresTitle: "ماذا تقدّم البراعم",
features: [
{
name: "تبرع يناسب الأطفال",
desc: "رحلات تبرع بسيطة مصممة لقلوب وعقول صغيرة.",
},
{
name: "الأسرة معاً",
desc: "يتبرع الآباء والأبناء جنباً إلى جنب ويتشاركون الأجر.",
},
{
name: "محتوى تعليمي",
desc: "قصص وأنشطة تشرح قيمة الصدقة.",
},
{
name: "أثر للأطفال",
desc: "قضايا تركّز على الأطفال والأيتام والتعليم.",
},
],
audienceTitle: "لمن هذه المبادرة",
audience: [
{
name: "الطفولة المبكرة",
desc: "مقدمات لطيفة عن العطف والمشاركة.",
},
{
name: "سن المدرسة",
desc: "تبرع موجّه مرتبط بقضايا واقعية مفهومة.",
},
{
name: "الأسر",
desc: "مساحة مشتركة للآباء والأبناء للتبرع معاً.",
},
],
},
serviceItems: { serviceItems: {
ghiras: "غراس", ghiras: "غراس",
zakat: "الزكاة", zakat: "الزكاة",
+79
View File
@@ -0,0 +1,79 @@
import { useLanguage } from "../contexts/LanguageContext";
import { Reveal } from "../components/Reveal";
import { Card, CardContent } from "@/components/ui/card";
import { Check, Sprout, Users } from "lucide-react";
export default function Baraem() {
const { t } = useLanguage();
return (
<div className="container mx-auto px-4 py-10">
{/* 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.baraem.introTitle}</h1>
<p className="leading-loose text-primary-foreground/90 max-w-4xl">
{t.baraem.intro}
</p>
</section>
</Reveal>
{/* What Buds offers */}
<Reveal>
<h2 className="text-xl font-bold text-foreground mb-6">{t.baraem.featuresTitle}</h2>
</Reveal>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-12">
{t.baraem.features.map((f, 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">
<Sprout className="w-5 h-5" />
</div>
<div>
<h3 className="text-base font-bold text-foreground mb-1">{f.name}</h3>
<p className="text-sm text-muted-foreground leading-relaxed">{f.desc}</p>
</div>
</CardContent>
</Card>
</Reveal>
))}
</div>
{/* Goals */}
<Reveal>
<div className="rounded-2xl bg-muted/40 border border-border p-8 mb-12">
<h3 className="text-lg font-bold text-primary mb-4">{t.baraem.goalsTitle}</h3>
<ul className="grid grid-cols-1 md:grid-cols-2 gap-3">
{t.baraem.goals.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>
</Reveal>
{/* Audience */}
<Reveal>
<h2 className="text-xl font-bold text-foreground mb-6">{t.baraem.audienceTitle}</h2>
</Reveal>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{t.baraem.audience.map((a, i) => (
<Reveal key={i} delay={(i % 3) * 0.08} className="h-full">
<Card className="h-full">
<CardContent className="p-6">
<div className="w-11 h-11 rounded-xl bg-primary/10 text-primary flex items-center justify-center mb-4">
<Users className="w-5 h-5" />
</div>
<h3 className="text-base font-bold text-foreground mb-1">{a.name}</h3>
<p className="text-sm text-muted-foreground leading-relaxed">{a.desc}</p>
</CardContent>
</Card>
</Reveal>
))}
</div>
</div>
);
}
+72
View File
@@ -0,0 +1,72 @@
import { useLanguage } from "../contexts/LanguageContext";
import { Reveal } from "../components/Reveal";
import { Card, CardContent } from "@/components/ui/card";
import { Check, Landmark } from "lucide-react";
export default function Waqf() {
const { t } = useLanguage();
return (
<div className="container mx-auto px-4 py-10">
{/* 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.waqf.introTitle}</h1>
<p className="leading-loose text-primary-foreground/90 max-w-4xl">
{t.waqf.intro}
</p>
</section>
</Reveal>
{/* Ways to contribute */}
<Reveal>
<h2 className="text-xl font-bold text-foreground mb-6">{t.waqf.waysTitle}</h2>
</Reveal>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-12">
{t.waqf.ways.map((w, 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">
<Landmark className="w-5 h-5" />
</div>
<div>
<h3 className="text-base font-bold text-foreground mb-1">{w.name}</h3>
<p className="text-sm text-muted-foreground leading-relaxed">{w.desc}</p>
</div>
</CardContent>
</Card>
</Reveal>
))}
</div>
{/* Fields + features */}
<Reveal>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-12">
<div>
<h3 className="text-lg font-bold text-primary mb-4">{t.waqf.fieldsTitle}</h3>
<ul className="space-y-3">
{t.waqf.fields.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>
<div>
<h3 className="text-lg font-bold text-primary mb-4">{t.waqf.featuresTitle}</h3>
<div className="space-y-4">
{t.waqf.features.map((f, i) => (
<div key={i}>
<h4 className="text-base font-bold text-foreground mb-1">{f.name}</h4>
<p className="text-sm text-muted-foreground leading-relaxed">{f.desc}</p>
</div>
))}
</div>
</div>
</div>
</Reveal>
</div>
);
}