EHSAN official look & auth (Task #5)

Reskin the EHSAN POC to match ehsan.sa and gate the admin area behind a
simple POC login.

- Official header: cropped EHSAN logo, nav (الرئيسية/الوقف/فرص التبرع/
  خدماتنا dropdown→طلب دعم/عن إحسان/براعم إحسان), login/cart/search icons,
  language toggle, mobile menu. Functional items link; rest are visual-only.
- Colors: green primary tuned + orange accent token added in index.css.
- Auth: AuthContext (localStorage, admin/admin) + login page; /admin and
  /whatsapp-log now behind a Protected wrapper redirecting to /login.
- Redesigned OpportunityCard (photo, green progress bar with %, category
  badge, تم جمع/المبلغ المتبقي columns, inline donate button + amount),
  used on home and opportunities pages.
- Two-step donate page (التفاصيل → الدفع): step indicator, presets
  100/50/10, custom amount (prefilled via ?amount=), "تبرع عن أهلك"
  checkbox, donor form (phone min 10 for OpenClaw loop).
- 8 need-type card images added; needImages helper maps need→image.
- Seed: added published opportunities (req-012..017) with partial progress
  to showcase cards; one kept near-full for an easy closed-loop demo.

Deviation (from code review): donate handler now ACCUMULATES collectedAmount
and clamps to target, validates finite/positive amount, and only advances to
the closed-loop pipeline when a case is fully funded (previously overwrote
and force-advanced — broke partial-progress cases). Donate buttons kept green
to match the reference; orange is an accent only.
This commit is contained in:
Replit Agent
2026-06-05 17:45:17 +00:00
parent 4db9f09195
commit 5d40b0d3c2
28 changed files with 1177 additions and 324 deletions
@@ -25,6 +25,31 @@ export const en = {
allOpportunities: "All Opportunities",
featuredCases: "Featured Cases",
donate: "Donate",
login: "Login",
logout: "Logout",
cart: "Cart",
},
nav: {
home: "Home",
waqf: "Endowment",
opportunities: "Donation Opportunities",
services: "Services",
about: "About EHSAN",
baraem: "EHSAN Buds",
requestSupport: "Request Support",
admin: "Admin Panel",
whatsappLog: "WhatsApp Log",
quickDonate: "Quick Donate",
},
auth: {
title: "Admin Login",
subtitle: "Sign in to access the management dashboard",
username: "Username",
password: "Password",
signIn: "Sign In",
error: "Invalid username or password",
demoHint: "Demo credentials: admin / admin",
loggedInAs: "Signed in as administrator",
},
home: {
heroTitle: "Closed Donation Loop POC",
@@ -102,6 +127,10 @@ export const en = {
target: "Target",
noOpportunities: "No opportunities are available right now.",
verified: "Verified",
collectedShort: "Collected",
remainingShort: "Remaining",
amountPlaceholder: "Donation amount",
generalProjects: "General Projects",
},
donate: {
title: "Complete Donation",
@@ -113,6 +142,18 @@ export const en = {
confirmDonation: "Confirm Donation",
successMessage: "Thank you for your donation. May Allah reward you.",
caseNotFound: "Case not found or no longer available.",
stepDetails: "Details",
stepPayment: "Payment",
detailsTitle: "Details",
amountTitle: "Donation Amount",
customAmount: "Amount value",
onBehalf: "Donate on behalf of family or friends and share the reward",
onBehalfName: "Name of the person you donate for",
donateNow: "Donate Now",
continueToPayment: "Continue to Payment",
backToDetails: "Back to Details",
paymentTitle: "Payment Details",
selectAmountError: "Please select or enter a valid amount.",
},
admin: {
title: "Admin Dashboard",
@@ -192,6 +233,31 @@ export const ar = {
allOpportunities: "جميع الفرص",
featuredCases: "الحالات المميزة",
donate: "تبرع",
login: "تسجيل الدخول",
logout: "تسجيل الخروج",
cart: "السلة",
},
nav: {
home: "الرئيسية",
waqf: "الوقف",
opportunities: "فرص التبرع",
services: "خدماتنا",
about: "عن إحسان",
baraem: "براعم إحسان",
requestSupport: "طلب دعم",
admin: "لوحة الإدارة",
whatsappLog: "سجل واتساب",
quickDonate: "تبرع سريع",
},
auth: {
title: "دخول الإدارة",
subtitle: "سجّل الدخول للوصول إلى لوحة الإدارة",
username: "اسم المستخدم",
password: "كلمة المرور",
signIn: "تسجيل الدخول",
error: "اسم المستخدم أو كلمة المرور غير صحيحة",
demoHint: "بيانات تجريبية: admin / admin",
loggedInAs: "تم تسجيل الدخول كمسؤول",
},
home: {
heroTitle: "إقفال دورة التبرع",
@@ -269,6 +335,10 @@ export const ar = {
target: "الهدف",
noOpportunities: "لا توجد فرص متاحة حالياً.",
verified: "موثق",
collectedShort: "تم جمع",
remainingShort: "المبلغ المتبقي",
amountPlaceholder: "مبلغ التبرع",
generalProjects: "المشاريع العامة",
},
donate: {
title: "إتمام التبرع",
@@ -280,6 +350,18 @@ export const ar = {
confirmDonation: "تأكيد التبرع",
successMessage: "شكراً لتبرعك. جزاك الله خيراً.",
caseNotFound: "الحالة غير موجودة أو لم تعد متاحة.",
stepDetails: "التفاصيل",
stepPayment: "الدفع",
detailsTitle: "التفاصيل",
amountTitle: "مبلغ التبرع",
customAmount: "قيمة المبلغ",
onBehalf: "تبرع عن أهلك أو أصدقائك وشاركهم الأجر",
onBehalfName: "اسم من تتبرع عنه",
donateNow: "تبرع الآن",
continueToPayment: "متابعة للدفع",
backToDetails: "رجوع للتفاصيل",
paymentTitle: "بيانات الدفع",
selectAmountError: "الرجاء اختيار أو إدخال مبلغ صحيح.",
},
admin: {
title: "لوحة الإدارة",
+23
View File
@@ -0,0 +1,23 @@
import electricity from "../assets/needs/electricity.png";
import water from "../assets/needs/water.png";
import food from "../assets/needs/food.png";
import health from "../assets/needs/health.png";
import housing from "../assets/needs/housing.png";
import refrigerator from "../assets/needs/refrigerator.png";
import air_conditioner from "../assets/needs/air_conditioner.png";
import court_order from "../assets/needs/court_order.png";
const NEED_IMAGES: Record<string, string> = {
electricity,
water,
food,
health,
housing,
refrigerator,
air_conditioner,
court_order,
};
export function getNeedImage(needType: string): string {
return NEED_IMAGES[needType] || food;
}