Build EHSAN Closed Donation Loop POC — full bilingual Arabic/English app

- Backend (api-server): Complete in-memory mock DB with 11 seed cases, 5 eligible
  beneficiaries, 3 donors, and WhatsApp log. All 14 API routes implemented across
  requests, donors, stats, and whatsapp-log. OpenClaw integration with OPENCLAW_SIMULATE
  toggle. UUID-based IDs. Full status machine (new → closed, 10 steps).

- Frontend (ehsan-poc): 8 pages fully implemented using all generated API hooks:
  Home (stats counters, 10-step workflow diagram), Request (form with eligibility
  result), Opportunities (card grid with progress bars), Donate (case summary +
  donor form), Admin (full data table with contextual action buttons), Track
  (10-step visual timeline in green), ThankYou (message form), WhatsApp Log
  (WhatsApp bubble preview + OpenClaw send button).

- Bilingual LanguageContext (AR/EN) with RTL/LTR toggle, localStorage persistence.
  EHSAN green palette (HSL 143), Tajawal font, fully responsive.
  TypeScript clean — zero errors.
This commit is contained in:
Replit Agent
2026-06-05 17:05:27 +00:00
parent 2da838bb66
commit 12111a9562
117 changed files with 12366 additions and 81 deletions
@@ -0,0 +1,267 @@
export const en = {
common: {
ehsan: "EHSAN",
home: "Home",
opportunities: "Opportunities",
requestSupport: "Request Support",
adminDashboard: "Admin Dashboard",
whatsappLog: "WhatsApp Log",
submit: "Submit",
cancel: "Cancel",
save: "Save",
loading: "Loading...",
error: "An error occurred",
success: "Success",
back: "Back",
confirm: "Confirm",
language: "العربية",
},
home: {
heroTitle: "Closed Donation Loop POC",
heroSubtitle: "A demonstration of the complete donation lifecycle, from beneficiary request to donor appreciation.",
totalRequests: "Total Requests",
totalCollected: "Total Collected",
totalClosed: "Closed Cases",
viewOpportunities: "View Opportunities",
workflowTitle: "Closed Donation Loop Workflow",
},
workflow: {
step1: "Request Submitted",
step2: "Eligibility Check",
step3: "Verified",
step4: "Published",
step5: "Donor Donated",
step6: "Support Delivered",
step7: "Receipt Confirmed",
step8: "Thank-You Submitted",
step9: "WhatsApp Sent",
step10: "Case Closed",
},
needTypes: {
electricity: "Electricity",
water: "Water",
food: "Food Basket",
health: "Healthcare",
housing: "Housing",
refrigerator: "Refrigerator",
air_conditioner: "Air Conditioner",
court_order: "Court Order",
},
sources: {
beneficiary: "Direct Beneficiary",
charity: "Charity Organization",
official: "Official Entity",
},
statuses: {
new: "New",
pending_review: "Pending Review",
verified: "Verified",
published: "Published",
donated: "Donated",
delivered: "Delivered",
receipt_confirmed: "Receipt Confirmed",
thank_you_submitted: "Thank You Submitted",
whatsapp_sent: "WhatsApp Sent",
closed: "Closed",
rejected: "Rejected",
},
request: {
title: "Request Support",
beneficiaryName: "Beneficiary Name",
nationalId: "National ID",
phone: "Phone Number",
source: "Request Source",
sourceName: "Source Name",
needType: "Type of Need",
amount: "Requested Amount",
description: "Description / Case Details",
submitSuccess: "Request submitted successfully. Case is pending review.",
},
opportunities: {
title: "Donation Opportunities",
filterByType: "Filter by need type",
all: "All Types",
donate: "Donate Now",
collected: "Collected",
remaining: "Remaining",
target: "Target",
},
donate: {
title: "Complete Donation",
donorName: "Donor Name",
donorPhone: "Phone Number",
donorEmail: "Email (Optional)",
amount: "Donation Amount",
confirmDonation: "Confirm Donation",
successMessage: "Thank you for your donation. May Allah reward you.",
},
admin: {
title: "Admin Dashboard",
caseId: "Case ID",
beneficiary: "Beneficiary",
status: "Status",
currentStep: "Current Step",
actions: "Actions",
verify: "Verify",
publish: "Publish",
deliver: "Deliver Support",
confirmReceipt: "Confirm Receipt",
close: "Close Case",
reject: "Reject",
rejectionReason: "Rejection Reason",
},
track: {
title: "Track Case",
caseTimeline: "Case Timeline",
},
thankYou: {
title: "Submit Thank You Message",
message: "Thank You Message",
submitLabel: "Send Message to Donor",
},
whatsapp: {
title: "WhatsApp Log",
donor: "Donor",
message: "Message",
status: "Status",
sentAt: "Sent At",
sendViaOpenClaw: "Send via OpenClaw",
pending: "Pending",
sent: "Sent",
failed: "Failed",
}
};
export const ar = {
common: {
ehsan: "إحسان",
home: "الرئيسية",
opportunities: "فرص التبرع",
requestSupport: "طلب دعم",
adminDashboard: "لوحة الإدارة",
whatsappLog: "سجل واتساب",
submit: "إرسال",
cancel: "إلغاء",
save: "حفظ",
loading: "جاري التحميل...",
error: "حدث خطأ",
success: "نجاح",
back: "رجوع",
confirm: "تأكيد",
language: "English",
},
home: {
heroTitle: "إقفال دورة التبرع",
heroSubtitle: "نموذج يوضح دورة التبرع الكاملة، من طلب المستفيد حتى شكر المتبرع.",
totalRequests: "إجمالي الطلبات",
totalCollected: "إجمالي التبرعات (ريال)",
totalClosed: "الحالات المغلقة",
viewOpportunities: "استعراض الفرص",
workflowTitle: "خطوات إقفال دورة التبرع",
},
workflow: {
step1: "مقدم الطلب",
step2: "التحقق من الاستحقاق",
step3: "موثق",
step4: "نشر الفرصة",
step5: "المتبرع",
step6: "تنفيذ الدعم",
step7: "تأكيد الاستلام",
step8: "رسالة شكر",
step9: "إرسال واتساب",
step10: "إغلاق الحالة",
},
needTypes: {
electricity: "كهرباء",
water: "ماء",
food: "سلة غذائية",
health: "صحة",
housing: "سكن",
refrigerator: "ثلاجة",
air_conditioner: "مكيف",
court_order: "حكم قضائي",
},
sources: {
beneficiary: "مستفيد مباشر",
charity: "جمعية خيرية",
official: "جهة رسمية",
},
statuses: {
new: "جديد",
pending_review: "قيد المراجعة",
verified: "موثق",
published: "منشور",
donated: "تم التبرع",
delivered: "تم التسليم",
receipt_confirmed: "تم تأكيد الاستلام",
thank_you_submitted: "رسالة الشكر مقدمة",
whatsapp_sent: "واتساب مرسل",
closed: "مغلق",
rejected: "مرفوض",
},
request: {
title: "تقديم طلب دعم",
beneficiaryName: "اسم المستفيد",
nationalId: "رقم الهوية",
phone: "رقم الجوال",
source: "مصدر الطلب",
sourceName: "اسم المصدر",
needType: "نوع الاحتياج",
amount: "المبلغ المطلوب",
description: "وصف الحالة / التفاصيل",
submitSuccess: "تم تقديم الطلب بنجاح. الحالة قيد المراجعة.",
},
opportunities: {
title: "فرص التبرع",
filterByType: "تصفية حسب نوع الاحتياج",
all: "جميع الأنواع",
donate: "تبرع الآن",
collected: "المجموع",
remaining: "المتبقي",
target: "الهدف",
},
donate: {
title: "إتمام التبرع",
donorName: "اسم المتبرع",
donorPhone: "رقم الجوال",
donorEmail: "البريد الإلكتروني (اختياري)",
amount: "مبلغ التبرع",
confirmDonation: "تأكيد التبرع",
successMessage: "شكراً لتبرعك. جزاك الله خيراً.",
},
admin: {
title: "لوحة الإدارة",
caseId: "رقم الحالة",
beneficiary: "المستفيد",
status: "الحالة",
currentStep: "الخطوة الحالية",
actions: "الإجراءات",
verify: "توثيق",
publish: "نشر",
deliver: "تنفيذ الدعم",
confirmReceipt: "تأكيد الاستلام",
close: "إغلاق الحالة",
reject: "رفض",
rejectionReason: "سبب الرفض",
},
track: {
title: "تتبع الحالة",
caseTimeline: "مسار الحالة",
},
thankYou: {
title: "تقديم رسالة الشكر",
message: "رسالة الشكر",
submitLabel: "إرسال الرسالة للمتبرع",
},
whatsapp: {
title: "سجل رسائل الواتساب",
donor: "المتبرع",
message: "الرسالة",
status: "الحالة",
sentAt: "وقت الإرسال",
sendViaOpenClaw: "إرسال عبر OpenClaw",
pending: "قيد الانتظار",
sent: "مرسل",
failed: "فشل",
}
};
+6
View File
@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}