// AdminView.jsx — لوحة تحكم الأدمن // التبويبات: نظرة عامة، المستخدمين، المحامون، الحجوزات، الاشتراكات، الخطط، الكوبونات // // الوصول: فقط للمستخدمين اللي is_admin=true (الـ wiring في Platform.html بيشيك على ده) /* ── Shared styles ─────────────────────────────────── */ const adminStyles = { card: { background: sanadColors.bgElevated, borderRadius: 12, boxShadow: '0 0 0 1px rgba(239,231,214,0.08), 0 2px 6px rgba(0,0,0,0.04)', }, emptyBox: { padding: '48px 24px', textAlign: 'center', background: sanadColors.bgElevated, borderRadius: 12, boxShadow: '0 0 0 1px rgba(239,231,214,0.08)', color: sanadColors.textMuted, fontSize: 14, }, btnPrimary: { padding: '7px 14px', borderRadius: 8, border: 'none', background: sanadColors.brand, color: '#fff', cursor: 'pointer', fontFamily: sanadFonts.arabic, fontSize: 12, fontWeight: 600, }, btnNeutral: { padding: '6px 14px', borderRadius: 8, border: `1px solid ${sanadColors.border}`, background: sanadColors.bgElevated, color: sanadColors.textSecondary, cursor: 'pointer', fontFamily: sanadFonts.arabic, fontSize: 12, fontWeight: 500, }, btnDanger: { padding: '6px 12px', borderRadius: 8, border: `1px solid ${sanadColors.error}`, background: 'transparent', color: sanadColors.error, cursor: 'pointer', fontFamily: sanadFonts.arabic, fontSize: 12, fontWeight: 500, }, pill: { display: 'inline-flex', alignItems: 'center', gap: 4, padding: '2px 9px', borderRadius: 9999, fontSize: 11, fontWeight: 600, }, tableHead: { fontSize: 11, fontWeight: 700, color: sanadColors.textMuted, padding: '10px 14px', textAlign: 'right', background: sanadColors.bgSubtle, letterSpacing: '0.02em', }, tableCell: { fontSize: 12.5, color: sanadColors.textPrimary, padding: '11px 14px', borderTop: `1px solid ${sanadColors.borderLight}`, textAlign: 'right', verticalAlign: 'middle', }, }; function fmtAdminDate(iso, withTime = false) { if (!iso) return '—'; try { const d = new Date(iso); return d.toLocaleString('ar-SA', withTime ? { dateStyle: 'short', timeStyle: 'short' } : { dateStyle: 'medium' }); } catch { return iso; } } const USER_TYPE_LABEL = { individual: 'فرد', lawyer: 'محامي', firm: 'مكتب', student: 'طالب', business: 'منشأة', }; const APPROVAL_LABEL = { pending: { label: 'قيد المراجعة', bg: 'rgba(224,160,82,0.14)', color: '#e0a052' }, approved: { label: 'معتمد', bg: 'rgba(95,191,138,0.16)', color: sanadColors.success }, rejected: { label: 'مرفوض', bg: 'rgba(255,138,138,0.12)', color: sanadColors.error }, }; const BOOKING_STATUS_LABEL = { requested: { label: 'مطلوب', bg: '#1d2a25', color: sanadColors.textSecondary }, awaiting_lawyer: { label: 'بانتظار المحامي', bg: 'rgba(224,160,82,0.14)', color: '#e0a052' }, approved: { label: 'بانتظار الدفع', bg: 'rgba(224,160,82,0.14)', color: '#e0a052' }, paid: { label: 'مدفوع', bg: 'rgba(95,191,138,0.16)', color: sanadColors.success }, active: { label: 'نشط', bg: sanadColors.brand, color: '#fff' }, done: { label: 'مكتمل', bg: '#1d2a25', color: sanadColors.textSecondary }, rejected: { label: 'مرفوض', bg: 'rgba(255,138,138,0.12)', color: sanadColors.error }, cancelled: { label: 'ملغي', bg: '#1d2a25', color: sanadColors.textSecondary }, }; /* ══════════════════════════════════════════════════════ * Tab 1: نظرة عامة (Overview + Activity chart) * ══════════════════════════════════════════════════════ */ function AdminOverviewTab({ onJumpTab }) { const [overview, setOverview] = React.useState(null); const [activity, setActivity] = React.useState(null); const [loading, setLoading] = React.useState(true); const [error, setError] = React.useState(null); const [days, setDays] = React.useState(7); React.useEffect(() => { let cancel = false; (async () => { setLoading(true); try { const [o, a] = await Promise.all([ window.api.admin.statsOverview(), window.api.admin.statsActivity(days), ]); if (!cancel) { setOverview(o); setActivity(a); setError(null); } } catch (e) { if (!cancel) setError(e.message || 'فشل جلب الإحصائيات'); } finally { if (!cancel) setLoading(false); } })(); return () => { cancel = true; }; }, [days]); if (loading) return
| # | الاسم | الإيميل | النوع | الحالة | سُجِّل | إجراءات |
|---|---|---|---|---|---|---|
| {u.id} |
{u.full_name || '—'}
|
{u.email} | {USER_TYPE_LABEL[u.user_type] || u.user_type} |
{u.is_active ? 'نشط' : 'معطّل'}
{u.is_admin && (
أدمن
)}
{u.is_verified && (
موثّق
)}
|
{fmtAdminDate(u.created_at)} |
|
| # | العميل | المحامي | النوع | الحالة | السعر | الموعد | طُلب |
|---|---|---|---|---|---|---|---|
| #{b.id} |
{b.client_name || `#${b.client_id}`}
{(b.client_email || b.client_phone) && (
{b.client_email}{b.client_phone ? ` · ${b.client_phone}` : ''}
)}
|
{b.lawyer_display_name || `#${b.lawyer_id}`}
|
{b.booking_type === 'immediate' ? 'فوري' : 'بموعد'} | {st.label} |
{b.price} ر.س
{b.paid_at && ✓ مدفوع }
|
{b.scheduled_at ? fmtAdminDate(b.scheduled_at, true) : '—'} | {fmtAdminDate(b.requested_at, true)} |
| # | المستخدم | الخطة | الدورة | الحالة | المبلغ | الكوبون | تنتهي |
|---|---|---|---|---|---|---|---|
| #{s.id} |
{s.user_full_name || '—'}
{s.user_email}
|
{s.plan_name_ar} | {s.billing_cycle === 'annual' ? 'سنوي' : 'شهري'} | {s.status === 'active' ? 'نشط' : s.status === 'cancelled' ? 'ملغي' : 'منتهي'} | {s.paid_amount} ر.س | {s.coupon_code || '—'} | {fmtAdminDate(s.expires_at)} |
| الكود | الخصم | الحالة | الاستخدام | الانتهاء | إجراءات |
|---|---|---|---|---|---|
| {c.code} | {c.discount_percent}% | {c.is_active ? 'نشط' : 'معطّل'} | {c.times_used} / {c.max_uses || '∞'} | {c.expires_at ? fmtAdminDate(c.expires_at) : 'بدون انتهاء'} |
| المحامي | الإيميل | الجلسات | الإجمالي | رسوم المنصة | الصافي المستحق |
|---|---|---|---|---|---|
|
{r.display_name}
محامي #{r.lawyer_id}
|
{r.user_email || '—'} |
{r.sessions_paid}
{r.sessions_completed} مكتملة
|
{r.gross_earned.toLocaleString('ar')} ر.س | {r.platform_fee.toLocaleString('ar')} ر.س | {r.net_payout.toLocaleString('ar')} ر.س |
{ADMIN_SHOW_MARKETPLACE ? 'إدارة المستخدمين، اعتماد المحامين، الحجوزات، التقييمات، المدفوعات، الاشتراكات، الخطط، والكوبونات' : 'تحديثات المصادر الرسميّة، ورسائل الدعم، والمستخدمون، والاشتراكات، والخطط، والكوبونات'}
{/* Tab nav */}