feat: N4/N5 — لوحتا أدمن المستأجر وخدمة العملاء (ويب)

نقاط سرد إدارية (خلف RolesGuard admin/dispatcher):
- GET /drivers/admin/list — سائقو المستأجر (بترشيح الاعتماد)
- GET /trips/admin/list — رحلات المستأجر (بترشيح الحالة/الراكب)
- GET /payouts/admin/list — سحوبات المستأجر (بترشيح الحالة)
- GET /admin/users/search?phone= — بحث خدمة العملاء (يُطبَّع ويُبحث بالفهرس
  الأعمى، لا يُعرض الرقم الخام)

اللوحتان (SPA مكتفية ذاتياً، vanilla JS، RTL):
- admin-web: دخول أدمن (هاتف+OTP) → سائقون (اعتماد) · رحلات · مراجعة وثائق
  (قبول/رفض) · سحوبات (تحويل/فشل). تحقّقت أنها تُصيَّر نظيفاً في المعاينة.
- service-web: بحث مستخدم برقمه → بياناته ورحلاته + تفاصيل رحلة بالمعرّف.

المصادقة هاتف+OTP → JWT بدور admin/dispatcher (نفس الموبايل). الحماية على
السيرفر؛ الواجهة عرض فقط. الوصول بـ?tenant=<slug>.

E2E موسّع: يثبت أن السائق/الراكب (غير أدمن) يُرفضان من النقاط الإدارية (403).
الشكاوى تحتاج وحدة complaints (لاحقاً). لا هجرة.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hamza-Ayed
2026-07-18 03:55:23 +03:00
co-authored by Claude Opus 4.8
parent c93be38622
commit 5cad6adbde
11 changed files with 380 additions and 5 deletions
+10
View File
@@ -253,6 +253,16 @@ async function main() {
const dupRate = await api('POST', `/trips/${tripId}/rate`, rider.token, { stars: 3 }, { allowError: true });
check('منع التقييم المزدوج', !dupRate.ok && dupRate.status === 400);
// ═══ 11.5 حراسة النقاط الإدارية (docs/22 — N4) ═══
console.log('\n11.5) حراسة لوحة الأدمن (RolesGuard)');
const adminAsDriver = await api('GET', '/drivers/admin/list', driver.token, null, { allowError: true });
check('السائق (غير أدمن) يُرفض من /drivers/admin/list', adminAsDriver.status === 403,
`status=${adminAsDriver.status}`);
const tripsAsRider = await api('GET', '/trips/admin/list', rider.token, null, { allowError: true });
check('الراكب يُرفض من /trips/admin/list', tripsAsRider.status === 403);
const searchAsRider = await api('GET', '/admin/users/search?phone=07900000000', rider.token, null, { allowError: true });
check('الراكب يُرفض من بحث خدمة العملاء', searchAsRider.status === 403);
// ═══ 12. السحب بـOTP (تدفّق خطوتين) ═══
console.log('\n12) سحب أرباح السائق (OTP خطوتين)');
// السائق يشحن محفظة أرباحه أولاً ليكون له رصيد للسحب