Files
Ehsan/artifacts/api-server/src/lib/mockDb.ts
T

620 lines
19 KiB
TypeScript
Raw Normal View History

import { v4 as uuidv4 } from "uuid";
export type NeedType =
| "electricity"
| "water"
| "food"
| "health"
| "housing"
| "refrigerator"
| "air_conditioner"
| "court_order";
export type RequestSource = "beneficiary" | "charity" | "official";
export type RequestStatus =
| "new"
| "pending_review"
| "verified"
| "published"
| "donated"
| "delivered"
| "receipt_confirmed"
| "thank_you_submitted"
| "whatsapp_sent"
| "closed"
| "rejected";
export type WhatsappStatus = "pending" | "sent" | "failed";
export interface DonationRequest {
id: string;
caseId: string;
beneficiaryName: string;
nationalId: string;
phone: string;
source: RequestSource;
sourceName: string;
needType: NeedType;
requestedAmount: number;
collectedAmount: number;
description: string;
status: RequestStatus;
currentStep: number;
donorId: string | null;
donorName: string | null;
thankYouMessage: string | null;
whatsappStatus: WhatsappStatus | null;
whatsappSentAt: string | null;
rejectionReason: string | null;
createdAt: string;
updatedAt: string;
}
export interface Donor {
id: string;
name: string;
phone: string;
email: string | null;
totalDonated: number;
donationCount: number;
}
export interface EligibilityRecord {
nationalId: string;
eligible: boolean;
}
export interface WhatsappLogEntry {
id: string;
caseId: string;
donorName: string;
donorPhone: string;
beneficiaryMessage: string;
whatsappMessage: string;
status: WhatsappStatus;
sentAt: string | null;
createdAt: string;
}
2026-06-06 22:27:47 +03:00
// Outbound WhatsApp "thank the donor" notifications, fired right after a
// successful donation. They are queued here (in-memory) and picked up by an
// external poller (OpenClaw) that actually sends the WhatsApp message and then
// marks the row as sent. Resets on container restart like all mock data.
export interface DonorNotification {
id: string;
caseId: string;
donorName: string;
donorPhone: string; // normalized to international form, e.g. 9665XXXXXXXX
message: string;
status: WhatsappStatus; // pending | sent | failed
sentAt: string | null;
createdAt: string;
}
// Fixed Arabic confirmation message sent to the donor after a successful
// donation. This is an outbound WhatsApp message (not in-app UI text), so it is
// intentionally a single fixed string and does not go through LanguageContext.
export const DONOR_THANK_YOU_MESSAGE =
"إحسانك يُثمر وعطاؤك يعين ..\n\nتمت عملية تبرعك عبر منصة إحسان بنجاح\n\n(والله يحب المحسنين)";
export const donorNotifications: DonorNotification[] = [];
// Normalize a Saudi phone number to WhatsApp international form.
// 05XXXXXXXX -> 9665XXXXXXXX
// 5XXXXXXXX -> 9665XXXXXXXX
// 9665XXXXXXXX -> 9665XXXXXXXX (unchanged)
// +966 5XXXXXXXX -> 9665XXXXXXXX
// Returns digits only (no '+'). Falls back to the cleaned digits if the shape
// is unexpected, so we never throw inside the donation flow.
export function normalizeSaudiPhone(raw: string): string {
const digits = String(raw || "").replace(/\D/g, "");
if (!digits) return "";
if (digits.startsWith("966")) return digits;
if (digits.startsWith("0")) return "966" + digits.slice(1);
if (digits.startsWith("5") && digits.length === 9) return "966" + digits;
return digits;
}
// ─── Eligibility Database ───────────────────────────────────────────────────
export const eligibilityDb: EligibilityRecord[] = [
{ nationalId: "1090512345", eligible: true },
{ nationalId: "1023456789", eligible: true },
{ nationalId: "2098765432", eligible: true },
{ nationalId: "1056789012", eligible: true },
{ nationalId: "2034567890", eligible: true },
{ nationalId: "1099999999", eligible: false },
];
// ─── Donors ─────────────────────────────────────────────────────────────────
export const donors: Donor[] = [
{
id: "donor-001",
name: "عبدالله المنصور",
phone: "0501234567",
email: "abdullah@example.com",
totalDonated: 5000,
donationCount: 2,
},
{
id: "donor-002",
name: "سارة الأحمد",
phone: "0556789012",
email: "sara@example.com",
totalDonated: 3000,
donationCount: 1,
},
{
id: "donor-003",
name: "محمد الشمري",
phone: "0589012345",
email: null,
totalDonated: 2500,
donationCount: 1,
},
];
// ─── Mock Requests ───────────────────────────────────────────────────────────
const now = new Date();
const d = (daysAgo: number) =>
new Date(now.getTime() - daysAgo * 86400000).toISOString();
export const requests: DonationRequest[] = [
{
id: "req-001",
caseId: "CASE-001",
beneficiaryName: "أحمد إبراهيم الحربي",
nationalId: "1090512345",
phone: "0501111111",
source: "beneficiary",
sourceName: "مستفيد مباشر",
needType: "electricity",
requestedAmount: 2400,
collectedAmount: 2400,
description: "فاتورة كهرباء متراكمة لمدة 6 أشهر، أب لأربعة أطفال",
status: "closed",
currentStep: 10,
donorId: "donor-001",
donorName: "عبدالله المنصور",
thankYouMessage: "جزاكم الله خيراً، وصلني الدعم وكان له أثر كبير عليّ وعلى أسرتي.",
whatsappStatus: "sent",
whatsappSentAt: d(1),
rejectionReason: null,
createdAt: d(30),
updatedAt: d(1),
},
{
id: "req-002",
caseId: "CASE-002",
beneficiaryName: "فاطمة علي السلمي",
nationalId: "1023456789",
phone: "0502222222",
source: "charity",
sourceName: "جمعية البر الخيرية",
needType: "water",
requestedAmount: 1800,
collectedAmount: 1800,
description: "فاتورة مياه متأخرة، أرملة تعيل ثلاثة أطفال",
status: "whatsapp_sent",
currentStep: 9,
donorId: "donor-002",
donorName: "سارة الأحمد",
thankYouMessage: "بارك الله فيكم، وصل الدعم في الوقت المناسب جداً.",
whatsappStatus: "sent",
whatsappSentAt: d(2),
rejectionReason: null,
createdAt: d(25),
updatedAt: d(2),
},
{
id: "req-003",
caseId: "CASE-003",
beneficiaryName: "خالد محمد الغامدي",
nationalId: "2098765432",
phone: "0503333333",
source: "official",
sourceName: "وزارة العدل",
needType: "food",
requestedAmount: 1200,
collectedAmount: 1200,
description: "سلة غذائية شهرية لعائلة من 6 أفراد",
status: "thank_you_submitted",
currentStep: 8,
donorId: "donor-003",
donorName: "محمد الشمري",
thankYouMessage: "شكراً جزيلاً، السلة الغذائية أفادتنا كثيراً.",
whatsappStatus: "pending",
whatsappSentAt: null,
rejectionReason: null,
createdAt: d(20),
updatedAt: d(3),
},
{
id: "req-004",
caseId: "CASE-004",
beneficiaryName: "مريم سالم العتيبي",
nationalId: "1056789012",
phone: "0504444444",
source: "charity",
sourceName: "الهلال الأحمر السعودي",
needType: "health",
requestedAmount: 5000,
collectedAmount: 5000,
description: "مصاريف علاج ومستلزمات طبية لمريضة مزمنة",
status: "receipt_confirmed",
currentStep: 7,
donorId: "donor-001",
donorName: "عبدالله المنصور",
thankYouMessage: null,
whatsappStatus: null,
whatsappSentAt: null,
rejectionReason: null,
createdAt: d(18),
updatedAt: d(4),
},
{
id: "req-005",
caseId: "CASE-005",
beneficiaryName: "عمر عبدالرحمن الدوسري",
nationalId: "2034567890",
phone: "0505555555",
source: "official",
sourceName: "شركة المياه الوطنية",
needType: "housing",
requestedAmount: 8000,
collectedAmount: 8000,
description: "إصلاحات طارئة في المسكن بعد تسرب المياه",
status: "delivered",
currentStep: 6,
donorId: "donor-002",
donorName: "سارة الأحمد",
thankYouMessage: null,
whatsappStatus: null,
whatsappSentAt: null,
rejectionReason: null,
createdAt: d(15),
updatedAt: d(5),
},
{
id: "req-006",
caseId: "CASE-006",
beneficiaryName: "نورة سعد القحطاني",
nationalId: "1090512345",
phone: "0506666666",
source: "beneficiary",
sourceName: "مستفيد مباشر",
needType: "refrigerator",
requestedAmount: 1500,
collectedAmount: 1500,
description: "ثلاجة منزلية لعائلة تفتقر لوسيلة حفظ الغذاء",
status: "donated",
currentStep: 5,
donorId: "donor-003",
donorName: "محمد الشمري",
thankYouMessage: null,
whatsappStatus: null,
whatsappSentAt: null,
rejectionReason: null,
createdAt: d(10),
updatedAt: d(6),
},
{
id: "req-007",
caseId: "CASE-007",
beneficiaryName: "سليمان ناصر الزهراني",
nationalId: "1023456789",
phone: "0507777777",
source: "charity",
sourceName: "جمعية التنمية الأسرية",
needType: "air_conditioner",
requestedAmount: 2000,
collectedAmount: 0,
description: "مكيف لمنزل في منطقة حارة، وجود أطفال ومسنين",
status: "published",
currentStep: 4,
donorId: null,
donorName: null,
thankYouMessage: null,
whatsappStatus: null,
whatsappSentAt: null,
rejectionReason: null,
createdAt: d(8),
updatedAt: d(7),
},
{
id: "req-008",
caseId: "CASE-008",
beneficiaryName: "حسن عبدالله الرشيدي",
nationalId: "2056789012",
phone: "0508888888",
source: "official",
sourceName: "وزارة العدل",
needType: "court_order",
requestedAmount: 3500,
collectedAmount: 0,
description: "سداد غرامة قضائية لتجنب الحجز على الممتلكات",
status: "verified",
currentStep: 3,
donorId: null,
donorName: null,
thankYouMessage: null,
whatsappStatus: null,
whatsappSentAt: null,
rejectionReason: null,
createdAt: d(6),
updatedAt: d(5),
},
{
id: "req-009",
caseId: "CASE-009",
beneficiaryName: "رنا طارق المالكي",
nationalId: "9999999999",
phone: "0509999999",
source: "beneficiary",
sourceName: "مستفيد مباشر",
needType: "food",
requestedAmount: 900,
collectedAmount: 0,
description: "سلة غذائية لأسرة محتاجة",
status: "pending_review",
currentStep: 2,
donorId: null,
donorName: null,
thankYouMessage: null,
whatsappStatus: null,
whatsappSentAt: null,
rejectionReason: null,
createdAt: d(2),
updatedAt: d(1),
},
{
id: "req-010",
caseId: "CASE-010",
beneficiaryName: "بدر محمد الجهني",
nationalId: "1099999999",
phone: "0511111111",
source: "charity",
sourceName: "الجمعية الخيرية",
needType: "electricity",
requestedAmount: 1800,
collectedAmount: 0,
description: "فاتورة كهرباء متراكمة",
status: "rejected",
currentStep: 2,
donorId: null,
donorName: null,
thankYouMessage: null,
whatsappStatus: null,
whatsappSentAt: null,
rejectionReason: "المستفيد غير مؤهل وفق قاعدة بيانات الاستحقاق",
createdAt: d(5),
updatedAt: d(4),
},
{
id: "req-011",
caseId: "CASE-011",
beneficiaryName: "أميرة خالد السبيعي",
nationalId: "1056789012",
phone: "0512222222",
source: "official",
sourceName: "شركة الكهرباء السعودية",
needType: "electricity",
requestedAmount: 3200,
collectedAmount: 0,
description: "فاتورة كهرباء لمنزل أرملة ذات أطفال صغار",
status: "new",
currentStep: 1,
donorId: null,
donorName: null,
thankYouMessage: null,
whatsappStatus: null,
whatsappSentAt: null,
rejectionReason: null,
createdAt: d(1),
updatedAt: d(0),
},
2026-06-05 17:45:17 +00:00
{
id: "req-012",
caseId: "CASE-012",
beneficiaryName: "هند فيصل العنزي",
nationalId: "1067890123",
phone: "0513333333",
source: "charity",
sourceName: "جمعية كافل الخيرية",
needType: "food",
requestedAmount: 1200,
collectedAmount: 780,
description: "سلة غذائية شهرية لأسرة مكونة من خمسة أفراد",
status: "published",
currentStep: 4,
donorId: null,
donorName: null,
thankYouMessage: null,
whatsappStatus: null,
whatsappSentAt: null,
rejectionReason: null,
createdAt: d(9),
updatedAt: d(2),
},
{
id: "req-013",
caseId: "CASE-013",
beneficiaryName: "صالح عوض الحارثي",
nationalId: "2078901234",
phone: "0514444444",
source: "official",
sourceName: "شركة الكهرباء السعودية",
needType: "electricity",
requestedAmount: 2600,
collectedAmount: 1850,
description: "سداد فاتورة كهرباء متراكمة لمنزل أسرة متعففة",
status: "published",
currentStep: 4,
donorId: null,
donorName: null,
thankYouMessage: null,
whatsappStatus: null,
whatsappSentAt: null,
rejectionReason: null,
createdAt: d(7),
updatedAt: d(1),
},
{
id: "req-014",
caseId: "CASE-014",
beneficiaryName: "لطيفة عبدالله الشهري",
nationalId: "1089012345",
phone: "0515555555",
source: "charity",
sourceName: "الهلال الأحمر السعودي",
needType: "health",
requestedAmount: 6000,
collectedAmount: 1500,
description: "مستلزمات طبية وأجهزة منزلية لمريضة مزمنة",
status: "published",
currentStep: 4,
donorId: null,
donorName: null,
thankYouMessage: null,
whatsappStatus: null,
whatsappSentAt: null,
rejectionReason: null,
createdAt: d(6),
updatedAt: d(1),
},
{
id: "req-015",
caseId: "CASE-015",
beneficiaryName: "ماجد سعيد القرني",
nationalId: "2090123456",
phone: "0516666666",
source: "beneficiary",
sourceName: "مستفيد مباشر",
needType: "water",
requestedAmount: 1400,
collectedAmount: 1120,
description: "سداد فاتورة مياه متأخرة لأسرة تعيلها أرملة",
status: "published",
currentStep: 4,
donorId: null,
donorName: null,
thankYouMessage: null,
whatsappStatus: null,
whatsappSentAt: null,
rejectionReason: null,
createdAt: d(5),
updatedAt: d(1),
},
{
id: "req-016",
caseId: "CASE-016",
beneficiaryName: "ريم ناصر المطيري",
nationalId: "1078901234",
phone: "0517777777",
source: "charity",
sourceName: "جمعية الإسكان التنموي",
needType: "housing",
requestedAmount: 9000,
collectedAmount: 3200,
description: "ترميم وحدة سكنية متضررة لأسرة ذات دخل محدود",
status: "published",
currentStep: 4,
donorId: null,
donorName: null,
thankYouMessage: null,
whatsappStatus: null,
whatsappSentAt: null,
rejectionReason: null,
createdAt: d(4),
updatedAt: d(1),
},
{
id: "req-017",
caseId: "CASE-017",
beneficiaryName: "عبدالعزيز فهد الرشيد",
nationalId: "2012345678",
phone: "0518888888",
source: "official",
sourceName: "جمعية تكافل",
needType: "air_conditioner",
requestedAmount: 2200,
collectedAmount: 2100,
description: "تأمين مكيف لمنزل يقطنه مسنون في منطقة شديدة الحرارة",
status: "published",
currentStep: 4,
donorId: null,
donorName: null,
thankYouMessage: null,
whatsappStatus: null,
whatsappSentAt: null,
rejectionReason: null,
createdAt: d(3),
updatedAt: d(1),
},
];
// ─── WhatsApp Log ────────────────────────────────────────────────────────────
export const whatsappLog: WhatsappLogEntry[] = [
{
id: "wa-001",
caseId: "CASE-001",
donorName: "عبدالله المنصور",
donorPhone: "0501234567",
beneficiaryMessage:
"جزاكم الله خيراً، وصلني الدعم وكان له أثر كبير عليّ وعلى أسرتي.",
whatsappMessage:
"السلام عليكم، نشكركم على تبرعكم عبر منصة إحسان.\nتم إيصال الدعم للمستفيد، وهذه رسالة الشكر من المستفيد:\n\"جزاكم الله خيراً، وصلني الدعم وكان له أثر كبير عليّ وعلى أسرتي.\"\nرقم الحالة: CASE-001",
status: "sent",
sentAt: d(1),
createdAt: d(2),
},
{
id: "wa-002",
caseId: "CASE-002",
donorName: "سارة الأحمد",
donorPhone: "0556789012",
beneficiaryMessage: "بارك الله فيكم، وصل الدعم في الوقت المناسب جداً.",
whatsappMessage:
"السلام عليكم، نشكركم على تبرعكم عبر منصة إحسان.\nتم إيصال الدعم للمستفيد، وهذه رسالة الشكر من المستفيد:\n\"بارك الله فيكم، وصل الدعم في الوقت المناسب جداً.\"\nرقم الحالة: CASE-002",
status: "sent",
sentAt: d(2),
createdAt: d(3),
},
{
id: "wa-003",
caseId: "CASE-003",
donorName: "محمد الشمري",
donorPhone: "0589012345",
beneficiaryMessage: "شكراً جزيلاً، السلة الغذائية أفادتنا كثيراً.",
whatsappMessage:
"السلام عليكم، نشكركم على تبرعكم عبر منصة إحسان.\nتم إيصال الدعم للمستفيد، وهذه رسالة الشكر من المستفيد:\n\"شكراً جزيلاً، السلة الغذائية أفادتنا كثيراً.\"\nرقم الحالة: CASE-003",
status: "pending",
sentAt: null,
createdAt: d(3),
},
];
// ─── Helper: Determine status after eligibility check ───────────────────────
export function checkEligibility(nationalId: string): {
eligible: boolean | null;
} {
const record = eligibilityDb.find((r) => r.nationalId === nationalId);
if (!record) return { eligible: null };
return { eligible: record.eligible };
}
// ─── Helper: Status → Step mapping ──────────────────────────────────────────
export const STATUS_STEP: Record<RequestStatus, number> = {
new: 1,
pending_review: 2,
verified: 3,
published: 4,
donated: 5,
delivered: 6,
receipt_confirmed: 7,
thank_you_submitted: 8,
whatsapp_sent: 9,
closed: 10,
rejected: 2,
};