(() => {
// U03 — portal-request inbox, follow-up log, lease-level Ejar review. Data is fictional.
const NS = window.AmlakPlusDesignSystem_b3aa85;
const { PageHeader, SummaryStrip, Money, StatusBadge, Button, Tabs, DataTable, Timeline, KeyValueList, Alert, Dialog, Field, Select, TextInput, Checkbox, SearchField, SegmentedControl, SaveStatus, StateView } = NS;
const D = window.AP_DATA;
const val = (e) => (e && e.target ? (e.target.type === 'checkbox' ? e.target.checked : e.target.value) : e);
const go = (r, p) => window.apGo && window.apGo(r, p);

D.portalRequests = D.portalRequests || [
  { id: 'r1', kind: 'TENANT_PAYMENT', status: 'PENDING', tenant: 'ريم الشهري', property: 'عمارة الملقا 12', unit: '301', leaseId: 'l7', amount: 2500, paidOn: '22/09/2026', ref: 'TRX-88213', account: 'حساب المكتب — الراجحي', proof: 'ايصال-تحويل-سبتمبر.pdf', sent: '23/09 · 09:40' },
  { id: 'r2', kind: 'TENANT_PAYMENT', status: 'PENDING', tenant: 'سعد الحارثي', property: 'عمارة الندى 3', unit: '201', leaseId: 'l6', amount: 2000, paidOn: '21/09/2026', ref: 'SNB-40551', account: 'حساب المالك — عبدالله السبيعي', proof: 'IMG_2210.jpg', sent: '22/09 · 21:15' },
  { id: 'r3', kind: 'TENANT_PAYMENT', status: 'PROCESSING', tenant: 'نورة العنزي', property: 'عمارة الندى 3', unit: '108', leaseId: 'l5', amount: 2750, paidOn: '22/09/2026', ref: 'TRX-88190', account: 'حساب المكتب — الراجحي', proof: 'transfer.pdf', sent: '22/09 · 14:02' },
  { id: 'r4', kind: 'OWNER_EXPENSE', status: 'APPROVED', owner: 'عبدالله بن سعد السبيعي', desc: 'إصلاح مضخة المياه — عمارة الندى 3', amount: 1850, note: 'موافق، مع إرفاق فاتورة الفني.', sent: '20/09', decided: '23/09 · 08:10', next: 'اعتماد المصروف ثم إثبات الدفع', to: 'expenses' },
  { id: 'r5', kind: 'OWNER_SETTLEMENT', status: 'REJECTED', owner: 'هيا الدوسري', desc: 'تصفية أغسطس 2026', amount: 18400, note: 'أتعاب أغسطس لا تطابق الاتفاق — أرجو المراجعة.', sent: '19/09', decided: '22/09 · 19:30', next: 'مراجعة التصفية وإعادة إرسالها', to: 'settlements' },
  { id: 'r6', kind: 'OWNER_EXPENSE', status: 'PENDING', owner: 'فهد المطيري', desc: 'دهان الواجهة — عمارة الحمراء', amount: 4200, sent: '21/09', next: 'بانتظار قرار المالك', to: 'expenses' },
  { id: 'r7', kind: 'TENANT_PAYMENT', status: 'ACCEPTED', tenant: 'منى الحربي', property: 'عمارة الملقا 12', unit: '110', leaseId: 'l7', amount: 3200, paidOn: '21/09/2026', ref: 'TRX-87904', account: 'حساب المكتب — الراجحي', proof: 'receipt.pdf', sent: '21/09 · 10:05', note: 'سُجلت دفعة بانتظار التحقق.', by: 'سارة الغامدي' },
  { id: 'r8', kind: 'TENANT_PAYMENT', status: 'REJECTED', tenant: 'عمر باسل', property: 'عمارة الندى 3', unit: '102', amount: 2500, paidOn: '18/09/2026', ref: 'TRX-0000', account: 'حساب المكتب — الراجحي', proof: 'screenshot.png', sent: '18/09 · 16:40', note: 'المرجع غير موجود في كشف الحساب.', by: 'أحمد — محاسب' }
];
const RS = { PENDING: ['warning', 'بانتظار المراجعة'], PROCESSING: ['info', 'جارٍ التسجيل'], ACCEPTED: ['success', 'سُجلت دفعة'], REJECTED: ['danger', 'مرفوض'], APPROVED: ['success', 'وافق المالك'] };
const OWNER_RS = { PENDING: ['neutral', 'عند المالك'], APPROVED: ['success', 'وافق المالك'], REJECTED: ['danger', 'رفض المالك'] };
const isTenant = (r) => r.kind === 'TENANT_PAYMENT';
const needsOffice = (r) => isTenant(r) ? r.status === 'PENDING' : ['APPROVED', 'REJECTED'].includes(r.status) && !r.handled;
const badge = (r) => { const [tone, label] = (isTenant(r) ? RS : OWNER_RS)[r.status]; return <StatusBadge size="sm" tone={tone} label={label} />; };
const KIND = { TENANT_PAYMENT: 'إثبات تحويل مستأجر', OWNER_EXPENSE: 'مصروف للموافقة', OWNER_SETTLEMENT: 'تصفية للموافقة' };

window.AP_FORMS.portalAccept = { title: 'مراجعة إثبات التحويل وتسجيله', sub: (c) => c.tenant + ' · ' + c.property + ' / ' + c.unit, width: 600,
  intro: { tone: 'warning', text: 'التسجيل يُنشئ دفعة «بانتظار التحقق» — لا يخفض المستحق ولا يصدر سندًا حتى يتأكد الوصول في كشف الحساب.' }, fields: [
  { t: 'summary', rows: (c) => [['المبلغ', c.amount, 'money'], ['تاريخ التحويل', c.paidOn], ['المرجع', c.ref], ['الحساب المحول إليه', c.account]] },
  { t: 'note', tone: 'neutral', text: 'نزّل الإثبات من القائمة وقارن المبلغ والمرجع والحساب قبل التسجيل.' },
  { k: 'note', l: 'ملاحظة المراجعة', t: 'area', req: true, ph: 'مثال: الإيصال واضح والمبلغ يطابق قسط سبتمبر' }
], submit: 'تسجيل كدفعة بانتظار التحقق', done: { title: 'سُجلت الدفعة بانتظار التحقق', text: 'تظهر الآن في «مدفوعات بانتظار التحقق». يُبلَّغ المستأجر بالحالة في البوابة.' } };
window.AP_FORMS.portalReject = { title: 'رفض الطلب', sub: (c) => (c.tenant || c.owner) + ' · ' + (c.ref || c.desc), width: 520, danger: true, fields: [
  { t: 'summary', rows: (c) => [['المبلغ', c.amount, 'money'], ['المرجع', c.ref || '—']] },
  { k: 'reason', l: 'سبب الرفض', t: 'select', req: true, o: ['المرجع غير موجود في كشف الحساب', 'المبلغ لا يطابق الإيصال', 'الإيصال غير واضح', 'الحساب المحول إليه خطأ', 'سبب آخر'], def: '' },
  { k: 'note', l: 'رسالة للمستأجر', t: 'area', req: true, ph: 'تظهر للمستأجر في البوابة' }
], submit: 'رفض الطلب', done: { title: 'رُفض الطلب', tone: 'warning', text: 'يرى المستأجر السبب في البوابة ويمكنه إرسال إثبات جديد. المستحق لم يتغير.' } };

function RequestRow({ r, onChange, compact }) {
  const t = isTenant(r);
  const accept = () => window.apOpen('portalAccept', { ...r, onSaved: () => onChange(r.id, { status: 'ACCEPTED', note: 'سُجلت دفعة بانتظار التحقق.' }) });
  const reject = () => window.apOpen('portalReject', { ...r, onSaved: (v) => onChange(r.id, { status: 'REJECTED', note: (v && v.reason) || 'مرفوض' }) });
  return (
    <li className="kit-work-row kit-req-row">
      <div className="kit-wr-who"><strong>{t ? r.tenant : r.owner}</strong><span>{t ? r.property + ' · وحدة ' + r.unit : r.desc}</span></div>
      <div className="kit-wr-amt"><Money value={r.amount} size="md" /><span>{KIND[r.kind]}</span></div>
      <div className="kit-wr-date"><strong>{t ? r.paidOn.slice(0, 5) : (r.decided || r.sent)}</strong><span>{t ? 'أُرسل ' + r.sent : r.decided ? 'تاريخ القرار' : 'أُرسل للمالك'}</span></div>
      <div className="kit-wr-next"><span style={{ justifySelf: 'start' }}>{badge(r)}</span>{t ? <span><bdi className="ap-ltr">{r.ref}</bdi> · {r.account}</span> : r.note ? <span>«{r.note}»</span> : <span>{r.next}</span>}</div>
      <div className="kit-wr-actions">
        {t && r.status === 'PENDING' && <><Button size="sm" icon="ShieldCheck" onClick={accept}>مراجعة وتسجيل</Button><Button size="sm" variant="ghost" onClick={reject}>رفض</Button></>}
        {t && r.status === 'PROCESSING' && <Button size="sm" variant="secondary" loading disabled>جارٍ التسجيل</Button>}
        {!t && needsOffice(r) && <Button size="sm" variant="secondary" icon={r.kind === 'OWNER_EXPENSE' ? 'Wallet' : 'HandCoins'} onClick={() => { onChange(r.id, { handled: true }); go(r.to); }}>{r.status === 'APPROVED' ? 'متابعة التنفيذ' : 'مراجعة وإعادة الإرسال'}</Button>}
        {t && <Button size="sm" variant="ghost" iconOnly icon="Download" label={'تنزيل الإثبات ' + r.proof} onClick={() => {}} />}
        {t && r.leaseId && !compact && <Button size="sm" variant="ghost" iconOnly icon="FileText" label="فتح العقد" onClick={() => go('lease', { id: r.leaseId })} />}
      </div>
    </li>
  );
}

function useRequests() {
  const [rows, setRows] = React.useState(D.portalRequests);
  const onChange = (id, patch) => { D.portalRequests = D.portalRequests.map((r) => r.id === id ? { ...r, ...patch } : r); setRows(D.portalRequests); };
  return [rows, onChange];
}

// Group on «متابعات اليوم» — only what needs an office decision today.
function TodayRequests() {
  const [rows, onChange] = useRequests();
  const open = rows.filter(needsOffice);
  if (!open.length) return null;
  return (
    <section className="kit-group" aria-labelledby="g-review">
      <header><h2 id="g-review"><StatusBadge tone="info" icon="Inbox" label="طلبات البوابة بانتظار المراجعة" /><span className="kit-count">{open.length}</span></h2><p>إثباتات تحويل أرسلها المستأجرون، وقرارات ملاك تحتاج خطوة من المكتب.</p>
        <Button size="sm" variant="ghost" onClick={() => go('requests')} style={{ marginInlineStart: 'auto' }}>كل الطلبات</Button></header>
      <ul>{open.map((r) => <RequestRow key={r.id} r={r} onChange={onChange} compact />)}</ul>
    </section>
  );
}

function PortalInboxScreen() {
  const [rows, onChange] = useRequests();
  const [tab, setTab] = React.useState('tenant');
  const [q, setQ] = React.useState('');
  const groups = {
    tenant: rows.filter((r) => isTenant(r) && ['PENDING', 'PROCESSING'].includes(r.status)),
    owner: rows.filter((r) => !isTenant(r) && (r.status === 'PENDING' || needsOffice(r))),
    closed: rows.filter((r) => isTenant(r) ? ['ACCEPTED', 'REJECTED'].includes(r.status) : r.handled)
  };
  const list = groups[tab].filter((r) => !q || [r.tenant, r.owner, r.property, r.ref, r.desc].join(' ').includes(q));
  return (
    <>
      <PageHeader title="طلبات البوابة" description="ما يرسله المستأجرون والملاك من البوابة. لا شيء هنا يغيّر المستحق قبل أن يراجعه المكتب." />
      <SummaryStrip items={[
        { label: 'إثباتات تحويل للمراجعة', amount: groups.tenant.filter((r) => r.status === 'PENDING').reduce((s, r) => s + r.amount, 0), tone: 'warning', note: groups.tenant.filter((r) => r.status === 'PENDING').length + ' طلبات · ليست تحصيلًا بعد', onClick: () => setTab('tenant'), selected: tab === 'tenant' },
        { label: 'قرارات ملاك تحتاج خطوة', value: rows.filter((r) => !isTenant(r) && needsOffice(r)).length, tone: 'info', note: 'موافقة أو رفض وصل من المالك', onClick: () => setTab('owner'), selected: tab === 'owner' },
        { label: 'عند الملاك', value: rows.filter((r) => !isTenant(r) && r.status === 'PENDING').length, tone: 'neutral', note: 'لا إجراء من المكتب حتى يقرر' }
      ]} />
      <Tabs style={{ marginTop: 18 }} label="نوع الطلبات" value={tab} onChange={setTab} tabs={[{ id: 'tenant', label: 'إثباتات المستأجرين', count: groups.tenant.length }, { id: 'owner', label: 'قرارات الملاك', count: groups.owner.length }, { id: 'closed', label: 'المنتهية', count: groups.closed.length }]} />
      <div className="kit-toolbar" style={{ marginTop: 14 }}><SearchField value={q} onChange={setQ} placeholder="الاسم أو العقار أو مرجع التحويل" style={{ flex: '1 1 260px', maxWidth: 420 }} /></div>
      <section className="kit-group">
        <header><h2>{{ tenant: 'إثباتات تحويل بانتظار المراجعة', owner: 'مصروفات وتصفيات أُرسلت للملاك', closed: 'طلبات انتهت مراجعتها' }[tab]}<span className="kit-count">{list.length}</span></h2>
          <p>{{ tenant: 'التسجيل يُنشئ دفعة بانتظار التحقق فقط. الرفض يحتاج سببًا يراه المستأجر.', owner: 'موافقة المالك مرتبطة بالمبلغ والنسخة المرسلة؛ أي تعديل يحتاج موافقة جديدة.', closed: 'محفوظة للأثر. الدفعات المسجلة تُتابع من «التحصيل والسندات».' }[tab]}</p></header>
        {!list.length ? <StateView kind="empty" icon="CircleCheck" title="لا طلبات هنا" text={q ? 'لا نتائج لهذا البحث.' : 'كل الطلبات في هذا القسم رُوجعت.'} /> : <ul>{list.map((r) => <RequestRow key={r.id} r={r} onChange={onChange} />)}</ul>}
      </section>
    </>
  );
}

D.followLog = D.followLog || {
  upcoming: [
    { date: '26/09', tenant: 'مؤسسة ركن البناء', where: 'مجمع الربيع التجاري / محل 3', action: 'مطالبة مكتوبة', who: 'ماجد الحربي', leaseId: 'l2', note: 'بعد عدم الرد مرتين' },
    { date: '28/09', tenant: 'سعد الحارثي', where: 'عمارة الندى 3 / 201', action: 'تذكير بالسداد', who: 'سارة الغامدي', leaseId: 'l6', note: 'وعد بالباقي 1,500' },
    { date: '30/09', tenant: 'خالد العتيبي', where: 'عمارة الندى 3 / 204', action: 'اتصال', who: 'سارة الغامدي', leaseId: 'l1', note: 'باقي الوعد بعد دفعة اليوم' },
    { date: '02/10', tenant: 'سلطان الزهراني', where: 'عمارة السلي 2 / 305', action: 'زيارة', who: 'ماجد الحربي', leaseId: 'l4', note: 'رصيد بعد المغادرة' }
  ],
  history: [
    { title: 'اتصال — رد ووعد بالسداد', date: '22/09/2026', actor: 'سارة الغامدي', tone: 'info', icon: 'Handshake', tenant: 'خالد العتيبي', who: 'سارة الغامدي', body: 'خالد العتيبي · عمارة الندى 3 / 204 — وعد 4,000 ر.س حتى 23/09.' },
    { title: 'واتساب — لم يرد', date: '21/09/2026', actor: 'ماجد الحربي', icon: 'Phone', tenant: 'مؤسسة ركن البناء', who: 'ماجد الحربي', body: 'مؤسسة ركن البناء · مجمع الربيع التجاري / محل 3.' },
    { title: 'حُددت متابعة 26/09 — مطالبة مكتوبة', date: '21/09/2026', actor: 'ماجد الحربي', icon: 'CalendarClock', tenant: 'مؤسسة ركن البناء', who: 'ماجد الحربي' },
    { title: 'زيارة — رد ورفض / اعترض', date: '20/09/2026', actor: 'ماجد الحربي', tone: 'warning', icon: 'Phone', tenant: 'سلطان الزهراني', who: 'ماجد الحربي', body: 'سلطان الزهراني · يعترض على رسوم التنظيف.' },
    { title: 'اتصال — رد ووعد بالسداد', date: '16/09/2026', actor: 'سارة الغامدي', tone: 'info', icon: 'Handshake', tenant: 'سعد الحارثي', who: 'سارة الغامدي', body: 'سعد الحارثي · عمارة الندى 3 / 201 — دفع 1,500 ووعد بالباقي 28/09.' },
    { title: 'اتصال — لم يرد', date: '09/09/2026', actor: 'سارة الغامدي', icon: 'Phone', tenant: 'خالد العتيبي', who: 'سارة الغامدي' }
  ]
};

function FollowLogScreen() {
  const L = D.followLog;
  const [tab, setTab] = React.useState('upcoming');
  const [who, setWho] = React.useState('all');
  const [q, setQ] = React.useState('');
  const f = (r) => (who === 'all' || r.who === 'سارة الغامدي') && (!q || (r.tenant + (r.where || '') + (r.body || '')).includes(q));
  const up = L.upcoming.filter(f), hist = L.history.filter(f);
  return (
    <>
      <PageHeader title="سجل المتابعات" description="المواعيد القادمة وكل تواصل سُجل مع المستأجرين. للاطلاع والتخطيط — العمل اليومي يبقى في «متابعات اليوم»."
        actions={<Button variant="secondary" icon="CalendarDays" onClick={() => go('today')}>العودة إلى متابعات اليوم</Button>} />
      <SummaryStrip items={[
        { label: 'مواعيد خلال 7 أيام', value: L.upcoming.filter((u) => u.date < '01/10' || u.date.endsWith('/09')).length, tone: 'brand', note: 'بعد اليوم', onClick: () => setTab('upcoming'), selected: tab === 'upcoming' },
        { label: 'تواصل مسجل — سبتمبر', value: L.history.length, tone: 'neutral', note: 'اتصال، واتساب، زيارة', onClick: () => setTab('history'), selected: tab === 'history' },
        { label: 'وعود مفتوحة', value: 2, tone: 'info', note: 'جزء من المتأخرات' }
      ]} />
      <Tabs style={{ marginTop: 18 }} label="السجل" value={tab} onChange={setTab} tabs={[{ id: 'upcoming', label: 'المواعيد القادمة', count: up.length }, { id: 'history', label: 'سجل التواصل', count: hist.length }]} />
      <div className="kit-toolbar" style={{ marginTop: 14 }}>
        <SearchField value={q} onChange={setQ} placeholder="اسم المستأجر أو العقار" style={{ flex: '1 1 260px', maxWidth: 420 }} />
        <SegmentedControl label="المسؤول" value={who} onChange={setWho} options={[{ value: 'all', label: 'كل الفريق' }, { value: 'me', label: 'مهامي' }]} />
      </div>
      {tab === 'upcoming' && (!up.length ? <StateView boxed kind="empty" icon="CalendarClock" title="لا مواعيد قادمة" text="حدد متابعة من متابعات اليوم أو من ملف العقد." /> : <DataTable caption="المواعيد القادمة" rows={up} columns={[
        { key: 'date', header: 'الموعد', render: (r) => <bdi className="ap-num ap-cell-strong">{r.date}</bdi> },
        { key: 't', header: 'المستأجر', render: (r) => <><span className="ap-cell-strong">{r.tenant}</span><small>{r.where}</small></> },
        { key: 'a', header: 'المطلوب', render: (r) => <>{r.action}<small>{r.note}</small></> },
        { key: 'w', header: 'المسؤول', render: (r) => r.who },
        { key: 'x', header: <span className="ap-sr-only">إجراءات</span>, render: (r) => <div className="kit-acts"><Button size="sm" variant="ghost" iconOnly icon="CalendarClock" label="تغيير الموعد" onClick={() => window.apOpen('followup', { tenant: r.tenant, subtitle: r.tenant + ' · ' + r.where })} /><Button size="sm" variant="ghost" iconOnly icon="FileText" label="فتح العقد" onClick={() => go('lease', { id: r.leaseId })} /></div> }
      ]} />)}
      {tab === 'history' && (!hist.length ? <StateView boxed kind="empty" icon="History" title="لا تواصل مسجل" text={q ? 'لا نتائج لهذا البحث.' : 'سجّل التواصل من متابعات اليوم.'} /> : <div className="kit-panel"><Timeline events={hist} /></div>)}
    </>
  );
}

// ---- Lease-level Ejar review (before expiry) ----
const daysTo = (end) => { const [d, m, y] = end.split('/').map(Number); return Math.round((new Date(y, m - 1, d) - new Date(2026, 8, 23)) / 864e5); };
const METHODS = [{ value: 'UNREVIEWED', label: 'لم تراجع' }, { value: 'OWNER_PAID', label: 'سددها المالك عن المستأجر' }, { value: 'TENANT_PAID', label: 'سداد موثق للمستأجر' }, { value: 'AGREED_SETTLEMENT', label: 'تسوية موثقة باتفاق الطرفين' }];
const RSTATUS = [{ value: 'UNKNOWN', label: 'غير معلوم' }, { value: 'ACTIVE', label: 'نشط' }, { value: 'ENDED', label: 'منتهٍ' }, { value: 'RENEWED', label: 'جُدد' }];
D.ejarReviews = D.ejarReviews || {
  l1: { asOf: '15/08/2026', status: 'ACTIVE', remaining: 9000, ref: 'كشف إيجار 15/08', by: 'منيرة القحطاني', note: 'المتبقي في إيجار يطابق الداخلي.', matches: { old: { amount: 3000, method: 'TENANT_PAID', ref: 'سند 000118' } } }
};
const reviewOf = (L) => D.ejarReviews[L.id] || null;
function reviewState(L, payments) {
  const days = daysTo(L.end), r = reviewOf(L);
  const ext = [...(L.history || []), ...payments].filter((p) => p.status === 'confirmed' && p.method !== 'إيجار');
  const matched = ext.reduce((s, p) => s + ((r && r.matches[p.id] && r.matches[p.id].amount) || 0), 0);
  const total = ext.reduce((s, p) => s + p.amount, 0);
  return { days, r, ext, unmatched: total - matched, alert: days <= 60, stale: !!r && daysTo(r.asOf) < -30 };
}
window.leaseNeedsReview = (L) => daysTo(L.end) <= 60 && L.status !== 'draft';

function EjarReviewDialog({ L, ext, onClose, onSaved }) {
  const r = reviewOf(L);
  const [v, setV] = React.useState({ asOf: '2026-09-23', end: L.end.split('/').reverse().join('-'), status: r ? r.status : 'UNKNOWN', remaining: r && r.remaining != null ? String(r.remaining) : '', ref: '', note: '', ok: false });
  const [m, setM] = React.useState(() => Object.fromEntries(ext.map((p) => [p.id, { amount: String((r && r.matches[p.id] && r.matches[p.id].amount) || 0), method: (r && r.matches[p.id] && r.matches[p.id].method) || 'UNREVIEWED', ref: (r && r.matches[p.id] && r.matches[p.id].ref) || '' }])));
  const [tried, setTried] = React.useState(false);
  const [state, setState] = React.useState('idle');
  const set = (k) => (e) => setV((x) => ({ ...x, [k]: val(e) }));
  const setRow = (id, k) => (e) => setM((x) => ({ ...x, [id]: { ...x[id], [k]: val(e) } }));
  const over = ext.filter((p) => Number(m[p.id].amount) > p.amount).map((p) => p.id);
  const missing = ['ref', 'note'].filter((k) => v[k].trim().length < 3);
  const save = () => { setTried(true); if (missing.length || over.length || !v.ok) return; setState('saving'); setTimeout(() => {
    D.ejarReviews[L.id] = { asOf: v.asOf.split('-').reverse().join('/'), status: v.status, remaining: v.remaining === '' ? null : Number(v.remaining), ref: v.ref, note: v.note, by: D.user.name, matches: Object.fromEntries(ext.map((p) => [p.id, { amount: Number(m[p.id].amount) || 0, method: m[p.id].method, ref: m[p.id].ref }])), prev: r };
    setState('saved'); setTimeout(onSaved, 400); }, 800); };
  return (
    <Dialog title="تسجيل مراجعة إيجار" subtitle={L.tenant + ' · ' + L.property + ' / ' + L.unit} onClose={onClose} busy={state === 'saving'} width={880}
      footer={<><SaveStatus state={state} /><div style={{ display: 'flex', gap: 8 }}><Button variant="secondary" onClick={onClose} disabled={state === 'saving'}>إلغاء</Button><Button loading={state === 'saving'} onClick={save}>حفظ المراجعة</Button></div></>}>
      <div style={{ display: 'grid', gap: 16 }}>
        <Alert tone="neutral">مطابقة يدوية لما يظهر في منصة إيجار. لا تنفذ سدادًا ولا إنهاءً في إيجار، ولا تُنشئ تحصيلًا جديدًا.</Alert>
        <div className="kit-form">
          <Field label="تاريخ المراجعة" required><TextInput type="date" value={v.asOf} onChange={set('asOf')} /></Field>
          <Field label="تاريخ انتهاء العقد في إيجار" required><TextInput type="date" value={v.end} onChange={set('end')} /></Field>
          <Field label="حالة العقد حسب إيجار"><Select value={v.status} onChange={set('status')} options={RSTATUS} /></Field>
          <Field label="المتبقي الظاهر في إيجار" optional hint="اتركه فارغًا إن لم تطّلع عليه — يظهر «غير معلوم»."><TextInput ltr inputMode="decimal" value={v.remaining} onChange={set('remaining')} placeholder="0" /></Field>
          <Field label="مرجع الكشف أو الإثبات" required error={tried && missing.includes('ref') ? 'اكتب مرجعًا من 3 أحرف على الأقل' : undefined}><TextInput value={v.ref} onChange={set('ref')} placeholder="مثال: كشف إيجار 23/09" /></Field>
          <Field label="ملاحظات والإجراء التالي" required error={tried && missing.includes('note') ? 'مطلوب' : undefined}><TextInput value={v.note} onChange={set('note')} placeholder="ما الذي وجدته وما المطلوب قبل إغلاق الملف" /></Field>
        </div>
        <div>
          <h3 className="kit-f-section" style={{ margin: '0 0 6px' }}>دفعات قُبضت خارج إيجار</h3>
          <p className="kit-scope" style={{ margin: '0 0 8px' }}>اكتب <b>إجمالي</b> ما طابقته لكل دفعة حتى هذه المراجعة — وليس الزيادة منذ المراجعة السابقة.</p>
          {!ext.length ? <StateView kind="empty" icon="CircleCheck" title="لا دفعات خارج إيجار" text="كل الدفعات المؤكدة لهذا العقد سُجلت عبر إيجار." /> : <DataTable caption="مطابقة الدفعات" rows={ext} columns={[
            { key: 'p', header: 'الدفعة', render: (p) => <><span className="ap-cell-strong"><Money value={p.amount} /></span><small><bdi className="ap-num">{p.date}</bdi> · سند {p.receipt} · {p.method}</small></> },
            { key: 'a', header: 'المطابق', render: (p) => <div style={{ width: 110 }}><TextInput ltr inputMode="decimal" aria-label={'المبلغ المطابق للسند ' + p.receipt} value={m[p.id].amount} onChange={setRow(p.id, 'amount')} />{over.includes(p.id) && <small className="kit-f-missing">أكبر من الدفعة</small>}</div> },
            { key: 'm', header: 'كيف عولجت في إيجار', render: (p) => <Select aria-label="طريقة المعالجة" value={m[p.id].method} onChange={setRow(p.id, 'method')} options={METHODS} /> },
            { key: 'r', header: 'مرجع الإثبات', render: (p) => <TextInput aria-label="مرجع إثبات المطابقة" value={m[p.id].ref} onChange={setRow(p.id, 'ref')} placeholder="اختياري" /> }
          ]} />}
        </div>
        <Checkbox label="راجعت الأرقام والمراجع. هذه مطابقة يدوية لا تنفذ سدادًا أو إنهاءً في إيجار." checked={v.ok} onChange={set('ok')} />
        {tried && !v.ok && <span className="kit-f-missing">أكّد المراجعة قبل الحفظ.</span>}
      </div>
    </Dialog>
  );
}

function LeaseEjarReview({ L, payments }) {
  const [n, setN] = React.useState(0);
  const [open, setOpen] = React.useState(false);
  const s = reviewState(L, payments), r = s.r;
  const label = (arr, x) => (arr.find((o) => o.value === x) || {}).label;
  const hist = [r, r && r.prev].filter(Boolean);
  return (
    <div style={{ display: 'grid', gap: 14 }} data-rev={n}>
      {s.alert && <Alert tone={s.days < 0 ? 'danger' : 'warning'}>{s.days < 0 ? 'انتهى العقد منذ ' + Math.abs(s.days) + ' يومًا' : 'ينتهي العقد خلال ' + s.days + ' يومًا'} — راجع الرصيد في إيجار وطابق الدفعات المقبوضة خارجه قبل إغلاق الملف أو التجديد.</Alert>}
      <SummaryStrip items={[
        { label: 'انتهاء العقد', value: L.end, tone: s.alert ? 'warning' : 'neutral', note: s.days < 0 ? 'انتهى منذ ' + Math.abs(s.days) + ' يوم' : 'باقٍ ' + s.days + ' يوم' },
        { label: 'المتبقي حسب آخر مراجعة', amount: r && r.remaining != null ? r.remaining : undefined, value: r && r.remaining != null ? undefined : 'غير معلوم', tone: 'neutral', note: r ? 'في ' + r.asOf : 'لم يراجع بعد' },
        { label: 'قبض خارج إيجار لم يطابق', amount: s.unmatched, tone: s.unmatched ? 'warning' : 'success', note: s.ext.length + ' دفعات · ليست دفعات جديدة' }
      ]} />
      <section className="kit-group" style={{ marginBottom: 0 }}>
        <header><h2>آخر مراجعة</h2><p>{r ? 'سجلها ' + r.by + (s.stale ? ' — تحتاج تحديثًا' : '') : 'لم تُسجل مراجعة لهذا العقد.'}</p><Button size="sm" icon="FileCheck" onClick={() => setOpen(true)} style={{ marginInlineStart: 'auto' }}>تسجيل مراجعة</Button></header>
        {r ? <div style={{ padding: 16 }}><KeyValueList items={[{ label: 'تاريخ المراجعة', value: <bdi className="ap-num">{r.asOf}</bdi> }, { label: 'حالة العقد في إيجار', value: label(RSTATUS, r.status) }, { label: 'المتبقي في إيجار', value: r.remaining == null ? 'غير معلوم' : <Money value={r.remaining} /> }, { label: 'المرجع', value: r.ref }, { label: 'الملاحظة', value: r.note }]} /></div>
          : <StateView kind="empty" icon="FileCheck" title="لا مراجعة بعد" text="سجّل ما يظهر في إيجار الآن — الرصيد وحالة العقد والدفعات المقبوضة خارجه." />}
      </section>
      {s.ext.length > 0 && <DataTable caption="دفعات خارج إيجار" rows={s.ext} columns={[
        { key: 'p', header: 'الدفعة', render: (p) => <><Money value={p.amount} /><small><bdi className="ap-num">{p.date}</bdi> · سند {p.receipt}</small></> },
        { key: 'a', header: 'المطابق', numeric: true, render: (p) => <Money value={(r && r.matches[p.id] && r.matches[p.id].amount) || 0} tone={r && r.matches[p.id] ? undefined : 'muted'} /> },
        { key: 'm', header: 'المعالجة في إيجار', render: (p) => { const x = r && r.matches[p.id]; return x && x.method !== 'UNREVIEWED' ? label(METHODS, x.method) : <StatusBadge size="sm" tone="warning" label="لم تراجع" showIcon={false} />; } },
        { key: 'r', header: 'مرجع الإثبات', render: (p) => (r && r.matches[p.id] && r.matches[p.id].ref) || '—' }
      ]} />}
      {hist.length > 1 && <div className="kit-panel"><h3 className="kit-h2" style={{ marginTop: 0 }}>المراجعات السابقة</h3><Timeline events={hist.map((h) => ({ title: 'مراجعة إيجار — ' + label(RSTATUS, h.status), date: h.asOf, actor: h.by, icon: 'FileCheck', body: (h.remaining == null ? 'المتبقي غير معلوم' : 'المتبقي ' + h.remaining.toLocaleString('en-US') + ' ر.س') + ' · ' + h.ref }))} /></div>}
      {open && <EjarReviewDialog L={L} ext={s.ext} onClose={() => setOpen(false)} onSaved={() => { setOpen(false); setN((x) => x + 1); }} />}
    </div>
  );
}

// Section on «مطابقة إيجار» (periodic): which leases need a review, linking to each lease file.
function EjarDueLeases() {
  const rows = D.leases.filter((l) => l.status !== 'draft' && daysTo(l.end) <= 60).map((l) => ({ ...l, days: daysTo(l.end), r: D.ejarReviews[l.id] }));
  return (
    <section className="kit-group">
      <header><h2>عقود تحتاج مراجعة قبل الانتهاء<span className="kit-count">{rows.length}</span></h2><p>خلال 60 يومًا من الانتهاء أو بعده. المراجعة تُسجل من ملف العقد.</p></header>
      {!rows.length ? <StateView kind="empty" icon="CircleCheck" title="لا عقود تقترب من الانتهاء" /> : <DataTable caption="عقود تحتاج مراجعة" rows={rows} columns={[
        { key: 't', header: 'العقد', render: (l) => <><span className="ap-cell-strong">{l.tenant}</span><small>{l.property} / {l.unit}</small></> },
        { key: 'e', header: 'الانتهاء', render: (l) => <><bdi className="ap-num">{l.end}</bdi><small>{l.days < 0 ? 'انتهى منذ ' + Math.abs(l.days) + ' يوم' : 'باقٍ ' + l.days + ' يوم'}</small></> },
        { key: 'r', header: 'آخر مراجعة', render: (l) => l.r ? <bdi className="ap-num">{l.r.asOf}</bdi> : <StatusBadge size="sm" tone="warning" label="لم يراجع" showIcon={false} /> },
        { key: 'a', header: '', render: (l) => <div className="kit-acts"><Button size="sm" variant="secondary" icon="FileCheck" onClick={() => go('lease', { id: l.id, tab: 'review' })}>فتح المراجعة</Button></div> }
      ]} />}
    </section>
  );
}

Object.assign(window, { apRequestCount: () => D.portalRequests.filter(needsOffice).length, TodayRequests, PortalInboxScreen, FollowLogScreen, LeaseEjarReview, EjarDueLeases });
})();
