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:
@@ -1,10 +1,211 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* EHSAN Closed Donation Loop API
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
export interface HealthStatus {
|
||||
status: string;
|
||||
}
|
||||
|
||||
export type DonationRequestSource = typeof DonationRequestSource[keyof typeof DonationRequestSource];
|
||||
|
||||
|
||||
export const DonationRequestSource = {
|
||||
beneficiary: 'beneficiary',
|
||||
charity: 'charity',
|
||||
official: 'official',
|
||||
} as const;
|
||||
|
||||
export type DonationRequestNeedType = typeof DonationRequestNeedType[keyof typeof DonationRequestNeedType];
|
||||
|
||||
|
||||
export const DonationRequestNeedType = {
|
||||
electricity: 'electricity',
|
||||
water: 'water',
|
||||
food: 'food',
|
||||
health: 'health',
|
||||
housing: 'housing',
|
||||
refrigerator: 'refrigerator',
|
||||
air_conditioner: 'air_conditioner',
|
||||
court_order: 'court_order',
|
||||
} as const;
|
||||
|
||||
export type DonationRequestStatus = typeof DonationRequestStatus[keyof typeof DonationRequestStatus];
|
||||
|
||||
|
||||
export const DonationRequestStatus = {
|
||||
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',
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @nullable
|
||||
*/
|
||||
export type DonationRequestWhatsappStatus = typeof DonationRequestWhatsappStatus[keyof typeof DonationRequestWhatsappStatus] | null;
|
||||
|
||||
|
||||
export const DonationRequestWhatsappStatus = {
|
||||
pending: 'pending',
|
||||
sent: 'sent',
|
||||
failed: 'failed',
|
||||
} as const;
|
||||
|
||||
export interface DonationRequest {
|
||||
id: string;
|
||||
caseId: string;
|
||||
beneficiaryName: string;
|
||||
nationalId: string;
|
||||
phone: string;
|
||||
source: DonationRequestSource;
|
||||
sourceName: string;
|
||||
needType: DonationRequestNeedType;
|
||||
requestedAmount: number;
|
||||
collectedAmount: number;
|
||||
description: string;
|
||||
status: DonationRequestStatus;
|
||||
currentStep: number;
|
||||
/** @nullable */
|
||||
donorId?: string | null;
|
||||
/** @nullable */
|
||||
donorName?: string | null;
|
||||
/** @nullable */
|
||||
thankYouMessage?: string | null;
|
||||
/** @nullable */
|
||||
whatsappStatus?: DonationRequestWhatsappStatus;
|
||||
/** @nullable */
|
||||
whatsappSentAt?: string | null;
|
||||
/** @nullable */
|
||||
rejectionReason?: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export type DonationRequestInputSource = typeof DonationRequestInputSource[keyof typeof DonationRequestInputSource];
|
||||
|
||||
|
||||
export const DonationRequestInputSource = {
|
||||
beneficiary: 'beneficiary',
|
||||
charity: 'charity',
|
||||
official: 'official',
|
||||
} as const;
|
||||
|
||||
export type DonationRequestInputNeedType = typeof DonationRequestInputNeedType[keyof typeof DonationRequestInputNeedType];
|
||||
|
||||
|
||||
export const DonationRequestInputNeedType = {
|
||||
electricity: 'electricity',
|
||||
water: 'water',
|
||||
food: 'food',
|
||||
health: 'health',
|
||||
housing: 'housing',
|
||||
refrigerator: 'refrigerator',
|
||||
air_conditioner: 'air_conditioner',
|
||||
court_order: 'court_order',
|
||||
} as const;
|
||||
|
||||
export interface DonationRequestInput {
|
||||
beneficiaryName: string;
|
||||
nationalId: string;
|
||||
phone: string;
|
||||
source: DonationRequestInputSource;
|
||||
sourceName: string;
|
||||
needType: DonationRequestInputNeedType;
|
||||
requestedAmount: number;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface DonationInput {
|
||||
donorName: string;
|
||||
donorPhone: string;
|
||||
/** @nullable */
|
||||
donorEmail?: string | null;
|
||||
amount: number;
|
||||
}
|
||||
|
||||
export interface ThankYouInput {
|
||||
beneficiaryName: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface RejectInput {
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
export interface WhatsappResult {
|
||||
success: boolean;
|
||||
message: string;
|
||||
simulated: boolean;
|
||||
/** @nullable */
|
||||
sentAt?: string | null;
|
||||
}
|
||||
|
||||
export interface Donor {
|
||||
id: string;
|
||||
name: string;
|
||||
phone: string;
|
||||
/** @nullable */
|
||||
email?: string | null;
|
||||
totalDonated: number;
|
||||
donationCount: number;
|
||||
}
|
||||
|
||||
export interface StatusCount {
|
||||
status: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface NeedTypeCount {
|
||||
needType: string;
|
||||
count: number;
|
||||
totalAmount: number;
|
||||
}
|
||||
|
||||
export interface Stats {
|
||||
totalRequests: number;
|
||||
totalDonated: number;
|
||||
totalCollected: number;
|
||||
totalClosed: number;
|
||||
pendingVerification?: number;
|
||||
activeOpportunities?: number;
|
||||
byStatus: StatusCount[];
|
||||
byNeedType: NeedTypeCount[];
|
||||
}
|
||||
|
||||
export type WhatsappLogEntryStatus = typeof WhatsappLogEntryStatus[keyof typeof WhatsappLogEntryStatus];
|
||||
|
||||
|
||||
export const WhatsappLogEntryStatus = {
|
||||
pending: 'pending',
|
||||
sent: 'sent',
|
||||
failed: 'failed',
|
||||
} as const;
|
||||
|
||||
export interface WhatsappLogEntry {
|
||||
id: string;
|
||||
caseId: string;
|
||||
donorName: string;
|
||||
donorPhone: string;
|
||||
beneficiaryMessage: string;
|
||||
whatsappMessage: string;
|
||||
status: WhatsappLogEntryStatus;
|
||||
/** @nullable */
|
||||
sentAt?: string | null;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export type ListRequestsParams = {
|
||||
status?: string;
|
||||
needType?: string;
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user