/**
* @fileoverview Unified Wallet UI, Ledger Statement & Points Converter
* @module ui/wallet-ui
* @description بناء واجهة المحفظة الرقمية الموحدة، كشف الحساب المالي، محول النقاط الفوري، وخزنة الكبسولات الزمنية.
*/
(global => {
'use strict';
const LEDGER_SALT = global.FritreeAppConstants?.SECURITY_SALTS?.LEDGER_SALT ||
"FritreeLedgerVerificationSalt_2026_Enterprise_Production_Wallet_SHA256_UltraSeal";
const LedgerState = {
activeTab: 'fsj4xyo5el',
filterAsset: 'all',
filterDirection: 'all',
searchQuery: '',
dateFrom: '',
dateTo: '',
isInitialized: false,
isClaimingCapsuleLock: false,
isSwappingPointsLock: false
};
/**
* بناء الهيكل البصري لقسم المحفظة الرقمية الموحدة
*/
const buildWalletLayoutElements = () => {
const contentArea = document.getElementById('s7ihkirolg');
if (!contentArea) return false;
const sidebar = document.getElementById('bmh59axx92');
if (sidebar && !sidebar.querySelector('[data-target="j2nzw12azj"]')) {
const navItem = document.createElement('div');
navItem.className = 'f2rj8bz2yt';
navItem.setAttribute('data-target', 'j2nzw12azj');
navItem.innerHTML = ' المحفظة الموحدة والخزنة';
sidebar.appendChild(navItem);
navItem.addEventListener('click', () => {
window.location.hash = 'j2nzw12azj';
});
}
const host = document.getElementById('fritree-dynamic-panes-host') || contentArea;
let pane = document.getElementById('j2nzw12azj');
if (pane) {
if (pane.parentElement !== host) {
host.appendChild(pane);
}
return true;
}
pane = document.createElement('section');
pane.className = 'rjiai07g77';
pane.id = 'j2nzw12azj';
pane.innerHTML = `
المحفظة وإدارة الأرصدة والكبسولات
رصيد الدولار (USD)
0.00 $
نقاط فيسبوك
0 FB
نقاط واتساب
0 WA
الكبسولات المجمدة
0 كبسولة
0.00 $
كشف الحساب والعمليات
تبديل النقاط
تحويل الرصيد
شحن الرصيد
خزنة الكبسولات
| تاريخ العملية |
تفاصيل الحركة |
نوع الأصل |
قيمة التغيير |
التوثيق |
|
لا توجد أي عمليات مالية مسجلة في المحفظة حالياً.
|
محول النقاط الفوري
معادلة التحويل: 2 FB = 1 WA
تبديل مباشر وفوري بين نقاط فيسبوك (FB) ونقاط واتساب (WA) بسعر التحويل المعتمد بدون أي رسوم.
قدرة الإرسال التقديرية للرصيد المستلم:
تغطي إرسال ~50 رسالة واتساب مباشرة
صفر عمولة تحويل
خزنة الكبسولات الزمنية
يتم تجميد الأرصدة داخل الكبسولة تلقائياً لفكها في التاريخ والوقت المستهدف
لا توجد كبسولات مجدولة حالياً.
`;
host.appendChild(pane);
return true;
};
/**
* ربط التبديل بين علامات تبويب المحفظة
*/
const bindWalletTabToggles = () => {
const walletPanel = document.getElementById('wefkbdgu3v');
if (!walletPanel) return;
const tabs = walletPanel.querySelectorAll('.pfe6btieb6 .n9cqm6ze7d[data-wallet-sub]');
const validWalletPaneIds = ['fsj4xyo5el', 'j3cvbn5t2y', 'sq5nufndhg', 'smzzdticgj', 'kflnifnkxu'];
tabs.forEach(tab => {
tab.addEventListener('click', () => {
tabs.forEach(t => t.classList.remove('vn5qn7dmpk'));
tab.classList.add('vn5qn7dmpk');
const targetPaneId = tab.getAttribute('data-wallet-sub');
LedgerState.activeTab = targetPaneId;
const panes = walletPanel.querySelectorAll('.gitkevh3xv');
panes.forEach(pane => {
if (validWalletPaneIds.includes(pane.id)) {
pane.style.display = (pane.id === targetPaneId) ? 'block' : 'none';
}
});
if (targetPaneId === 'j3cvbn5t2y') {
if (global.FritreeWalletExporter && typeof global.FritreeWalletExporter.init === 'function') {
global.FritreeWalletExporter.init();
}
} else if (targetPaneId === 'sq5nufndhg') {
if (global.FritreeWalletRedeemer && typeof global.FritreeWalletRedeemer.init === 'function') {
global.FritreeWalletRedeemer.init();
}
} else if (targetPaneId === 'kflnifnkxu') {
renderConverterUI();
} else if (targetPaneId === 'fsj4xyo5el') {
renderLedgerTable();
} else if (targetPaneId === 'smzzdticgj') {
renderTimeCapsulesList();
}
});
});
};
const signTransactionReceipt = async (txId, amount, type, desc, timestamp) => {
const payloadString = `${txId}:${amount}:${type}:${desc}:${timestamp}:${LEDGER_SALT}`;
if (global.FritreeCrypto && typeof global.FritreeCrypto.sha256 === 'function') {
return await global.FritreeCrypto.sha256(payloadString);
}
return "tx_sig_fallback_" + txId;
};
const verifyTransactionReceipt = async (tx) => {
if (!tx || !tx.id || !tx.signature) return false;
const computedSignature = await signTransactionReceipt(tx.id, tx.amount, tx.type, tx.desc, tx.date);
return computedSignature === tx.signature;
};
const auditTransactionHistory = async () => {
try {
const pointsTransactions = await global.FritreeStorage.get('pointsTransactions', []);
if (!Array.isArray(pointsTransactions) || pointsTransactions.length === 0) return true;
let changed = false;
for (let i = 0; i < pointsTransactions.length; i++) {
const tx = pointsTransactions[i];
if (tx.signature) {
const isValid = await verifyTransactionReceipt(tx);
if (!isValid) {
tx.signature = await signTransactionReceipt(tx.id, tx.amount, tx.type, tx.desc, tx.date);
changed = true;
}
} else {
tx.signature = await signTransactionReceipt(tx.id, tx.amount, tx.type, tx.desc, tx.date);
changed = true;
}
}
if (changed) {
await global.FritreeStorage.set('pointsTransactions', pointsTransactions);
}
return true;
} catch (e) {
console.error("[مدقق المحفظة] فشل تدقيق توقيع سجل المعاملات المالية:", e);
return false;
}
};
/**
* رسم جدول كشف الحساب والعمليات المالية
*/
const renderLedgerTable = async () => {
const tbody = document.getElementById('fcgxo3g6pn');
if (!tbody) return;
await auditTransactionHistory();
const pointsTransactions = await global.FritreeStorage.get('pointsTransactions', []);
const searchVal = (document.getElementById('hwhpohdf9p')?.value || '').toLowerCase().trim();
const assetFilter = document.getElementById('rwvvh5aw1o')?.value || 'all';
const dirFilter = document.getElementById('ydho523nfo')?.value || 'all';
const dateFrom = document.getElementById('nzekdiqxul')?.value || '';
const dateTo = document.getElementById('blve5jub6v')?.value || '';
let filtered = pointsTransactions.filter(tx => {
const matchesSearch = !searchVal ||
(tx.desc && tx.desc.toLowerCase().includes(searchVal)) ||
(tx.id && tx.id.toLowerCase().includes(searchVal)) ||
(tx.amount && String(tx.amount).includes(searchVal));
let matchesAsset = true;
if (assetFilter === 'usd') matchesAsset = !tx.type.includes('fb') && !tx.type.includes('wa');
else if (assetFilter === 'fb') matchesAsset = tx.type.includes('fb');
else if (assetFilter === 'wa') matchesAsset = tx.type.includes('wa');
let matchesDir = true;
const isAdd = tx.type === 'add' || tx.type === 'add_fb' || tx.type === 'add_wa' || tx.type === 'swap_in';
if (dirFilter === 'inflow') matchesDir = isAdd;
else if (dirFilter === 'outflow') matchesDir = !isAdd;
let matchesDate = true;
if (dateFrom && tx.date) matchesDate = matchesDate && new Date(tx.date) >= new Date(dateFrom);
if (dateTo && tx.date) matchesDate = matchesDate && new Date(tx.date) <= new Date(dateTo + 'T23:59:59');
return matchesSearch && matchesAsset && matchesDir && matchesDate;
});
const summaryLbl = document.getElementById('kfbbnqp3gb');
if (summaryLbl) {
summaryLbl.innerHTML = ` إجمالي المعاملات المطابقة: ${filtered.length.toLocaleString('en-US')} عملية (من أصل ${pointsTransactions.length.toLocaleString('en-US')})`;
}
tbody.innerHTML = '';
if (filtered.length === 0) {
tbody.innerHTML = `
|
لا توجد أي عمليات مالية مسجلة في المحفظة حالياً.
|
`;
return;
}
const list = [...filtered].reverse();
list.forEach(tx => {
const tr = document.createElement('tr');
tr.style.cssText = 'border-bottom: 1px solid var(--border);';
const isAdd = tx.type === 'add' || tx.type === 'add_fb' || tx.type === 'add_wa' || tx.type === 'swap_in';
const typeColor = isAdd ? 'var(--success-hover)' : 'var(--danger)';
const prefix = isAdd ? '+' : '-';
let assetName = 'دولار ($)';
let displayAmt = '';
if (tx.type.includes('fb')) {
assetName = 'نقاط FB';
displayAmt = `${parseInt(tx.amount, 10).toLocaleString('en-US')} FB`;
} else if (tx.type.includes('wa')) {
assetName = 'نقاط WA';
displayAmt = `${parseInt(tx.amount, 10).toLocaleString('en-US')} WA`;
} else {
displayAmt = `$${parseFloat(tx.amount).toFixed(2)}`;
}
tr.innerHTML = `
${new Date(tx.date).toLocaleString('en-US')} |
${tx.desc} |
${assetName}
|
${prefix}${displayAmt}
|
|
`;
tbody.appendChild(tr);
});
};
/**
* رسم قائمة الكبسولات الزمنية
*/
const renderTimeCapsulesList = async () => {
const container = document.getElementById('e8w1onnjl1');
const badgeCount = document.getElementById('f8q8ytlir2');
const sumValLabel = document.getElementById('nhcriyrkc8');
if (!container) return;
const capsules = await global.FritreeStorage.get('local_time_capsules', []) || [];
if (badgeCount) badgeCount.textContent = `${capsules.length.toLocaleString('en-US')} كبسولة`;
let totalUsdValue = 0;
capsules.forEach(c => { totalUsdValue += (parseFloat(c.usd) || 0); });
if (sumValLabel) sumValLabel.textContent = totalUsdValue.toFixed(2);
container.innerHTML = '';
if (capsules.length === 0) {
container.innerHTML = `
لا توجد كبسولات زمنية مجدولة حالياً.
`;
return;
}
const now = Date.now();
capsules.forEach(cap => {
const unlockTime = new Date(cap.unlockDate).getTime();
const isUnlocked = now >= unlockTime;
const card = document.createElement('div');
card.style.cssText = `
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 12px 16px;
display: flex;
justify-content: space-between;
align-items: center;
direction: rtl;
text-align: right;
box-shadow: var(--shadow-xs);
box-sizing: border-box;
flex-wrap: wrap;
gap: 8px;
`;
card.innerHTML = `
${cap.title || 'كبسولة شحن زمنية'}
المبلغ المكتوم: $${parseFloat(cap.usd || 0).toFixed(2)} | FB: ${(cap.fb || 0).toLocaleString('en-US')} | WA: ${(cap.wa || 0).toLocaleString('en-US')}
تاريخ الفتح المجدول: ${new Date(cap.unlockDate).toLocaleString('en-US')}
`;
const claimBtn = card.querySelector('.dlch9mdf6r');
if (claimBtn && isUnlocked) {
claimBtn.onclick = async () => {
await claimTimeCapsule(cap.id);
};
}
container.appendChild(card);
});
};
/**
* فك واسترداد رصيد كبسولة زمنية
*/
const claimTimeCapsule = async (capsuleId) => {
if (LedgerState.isClaimingCapsuleLock) return;
LedgerState.isClaimingCapsuleLock = true;
try {
let capsules = await global.FritreeStorage.get('local_time_capsules', []) || [];
const capIndex = capsules.findIndex(c => c.id === capsuleId);
if (capIndex === -1) {
alert("خطأ: تعذر العثور على الكبسولة أو تم استردادها سابقاً.");
return;
}
const cap = capsules[capIndex];
const unlockTime = new Date(cap.unlockDate).getTime();
if (Date.now() < unlockTime) {
alert("موعد فتح الكبسولة لم يحن بعد!");
return;
}
const usd = parseFloat(cap.usd) || 0;
const fb = parseInt(cap.fb, 10) || 0;
const wa = parseInt(cap.wa, 10) || 0;
capsules.splice(capIndex, 1);
await global.FritreeStorage.set('local_time_capsules', capsules);
if (usd > 0) await global.FritreeAppState.addPoints(usd, `فك كبسولة زمنية مجدولة: +$${usd.toFixed(2)}`);
if (fb > 0) await global.FritreeAppState.addFbCards(fb, `فك كبسولة زمنية مجدولة: +${fb} FB`);
if (wa > 0) await global.FritreeAppState.addWaCards(wa, `فك كبسولة زمنية مجدولة: +${wa} WA`);
await global.FritreeAppState.saveAccountData();
if (global.FritreeShellLayout) global.FritreeShellLayout.updatePointsUI();
alert(`تم فك الكبسولة واسترداد الأرصدة بنجاح!\n+$${usd.toFixed(2)} USD | +${fb} FB | +${wa} WA`);
await renderTimeCapsulesList();
await renderLedgerTable();
} catch (e) {
console.error("[Fritree Capsule Claim] Failed to unlock capsule:", e);
alert("حدث خطأ أثناء محاولة استرداد الكبسولة.");
} finally {
LedgerState.isClaimingCapsuleLock = false;
}
};
/**
* تحديث حاسبة تبديل النقاط
*/
const renderConverterUI = () => {
const srcSelect = document.getElementById('x1w33rae0d');
const tgtSelect = document.getElementById('mn39n9mx2i');
const amountInp = document.getElementById('xsy3e7r2f4');
const displayInp = document.getElementById('kwj16mxlu6');
const ratioLabel = document.getElementById('cuin29h7xe');
const capacityPreview = document.getElementById('kv6iccvkxs');
if (!srcSelect || !tgtSelect || !amountInp || !displayInp) return;
let srcVal = srcSelect.value || 'fb';
let tgtVal = tgtSelect.value || 'wa';
if (srcVal === tgtVal) {
tgtVal = srcVal === 'fb' ? 'wa' : 'fb';
tgtSelect.value = tgtVal;
}
const srcAmount = Math.max(1, parseInt(amountInp.value, 10) || 1);
amountInp.value = srcAmount;
let calculatedTarget = 0;
let ratioText = "2 FB = 1 WA";
let capacityText = "تغطية إرسال تقديرية للرصيد المستلم";
if (srcVal === 'fb' && tgtVal === 'wa') {
calculatedTarget = Math.floor(srcAmount * 0.5);
ratioText = "2 FB = 1 WA (معادلة التحويل الرسمية)";
capacityText = calculatedTarget > 0 ?
`تغطي إرسال حوالي ${calculatedTarget.toLocaleString('en-US')} رسالة واتساب جماعية للعملاء` :
`أدنى كمية مطلوبة للتحويل هي 2 نقاط فيسبوك لإنتاج 1 نقطة واتساب.`;
} else if (srcVal === 'wa' && tgtVal === 'fb') {
calculatedTarget = Math.floor(srcAmount * 2.0);
ratioText = "1 WA = 2 FB (معادلة التحويل الرسمية)";
capacityText = `تغطي إرسال حوالي ${calculatedTarget.toLocaleString('en-US')} منشور بمجموعات فيسبوك المستهدفة`;
}
displayInp.value = `${calculatedTarget.toLocaleString('en-US')} ${tgtVal.toUpperCase()}`;
if (ratioLabel) ratioLabel.innerHTML = ` ${ratioText}`;
if (capacityPreview) capacityPreview.textContent = capacityText;
};
/**
* تنفيذ تبديل النقاط بين فيسبوك وواتساب
*/
const executePointsSwap = async () => {
if (LedgerState.isSwappingPointsLock) return;
const srcSelect = document.getElementById('x1w33rae0d');
const tgtSelect = document.getElementById('mn39n9mx2i');
const amountInp = document.getElementById('xsy3e7r2f4');
if (!srcSelect || !tgtSelect || !amountInp) return;
const srcAsset = srcSelect.value;
const tgtAsset = tgtSelect.value;
const srcAmount = parseInt(amountInp.value, 10) || 0;
if (srcAsset === tgtAsset || srcAmount <= 0) {
alert("يرجى إدخال كمية نقاط أكبر من الصفر للتحويل!");
return;
}
LedgerState.isSwappingPointsLock = true;
try {
const res = await global.FritreeAppState.convertPoints(srcAsset, tgtAsset, srcAmount);
if (res && res.success) {
renderConverterUI();
await renderLedgerTable();
if (global.FritreeShellLayout) global.FritreeShellLayout.updatePointsUI();
alert(`تم تحويل النقاط بنجاح!\nتمت إضافة +${res.targetAmount.toLocaleString('en-US')} ${tgtAsset.toUpperCase()} لحسابك فوراً.`);
} else {
alert(res?.error || "حدث خطأ أثناء إجراء التحويل.");
}
} catch (e) {
console.error("[Points Converter] Swap execution failed:", e);
alert("حدث خطأ أثناء إجراء التحويل.");
} finally {
LedgerState.isSwappingPointsLock = false;
}
};
/**
* ربط أحداث المحول والفلاتر
*/
const bindConverterAndFilterEvents = () => {
const srcSelect = document.getElementById('x1w33rae0d');
const tgtSelect = document.getElementById('mn39n9mx2i');
const amountInp = document.getElementById('xsy3e7r2f4');
const btnMax = document.getElementById('ffeime7ecn');
const btnInvert = document.getElementById('btn-invert-swap-direction');
const btnSwap = document.getElementById('btn-execute-points-swap');
if (srcSelect) srcSelect.onchange = () => { if (tgtSelect) tgtSelect.value = srcSelect.value === 'fb' ? 'wa' : 'fb'; renderConverterUI(); };
if (tgtSelect) tgtSelect.onchange = () => { if (srcSelect) srcSelect.value = tgtSelect.value === 'fb' ? 'wa' : 'fb'; renderConverterUI(); };
if (amountInp) amountInp.oninput = renderConverterUI;
if (btnMax) {
btnMax.onclick = () => {
const src = srcSelect.value;
const active = src === 'fb' ? global.FritreeAppState.getFbPoints() : global.FritreeAppState.getWaPoints();
amountInp.value = active > 0 ? active : 1;
renderConverterUI();
};
}
if (btnInvert) {
btnInvert.onclick = (e) => {
e.preventDefault();
const oldSrc = srcSelect.value;
srcSelect.value = tgtSelect.value;
tgtSelect.value = oldSrc;
renderConverterUI();
};
}
if (btnSwap) btnSwap.onclick = executePointsSwap;
const searchInput = document.getElementById('hwhpohdf9p');
const assetFilter = document.getElementById('rwvvh5aw1o');
const dirFilter = document.getElementById('ydho523nfo');
const dateFrom = document.getElementById('nzekdiqxul');
const dateTo = document.getElementById('blve5jub6v');
const btnExportCSV = document.getElementById('jd0salbsv1');
if (searchInput) searchInput.oninput = renderLedgerTable;
if (assetFilter) assetFilter.onchange = renderLedgerTable;
if (dirFilter) dirFilter.onchange = renderLedgerTable;
if (dateFrom) dateFrom.onchange = renderLedgerTable;
if (dateTo) dateTo.onchange = renderLedgerTable;
if (btnExportCSV) {
btnExportCSV.onclick = async () => {
const txs = await global.FritreeStorage.get('pointsTransactions', []);
if (txs.length === 0) { alert('سجل كشف الحساب فارغ.'); return; }
let csv = '\ufeffتاريخ العملية,بيان الحركة المالية,نوع الأصل,قيمة التغيير,توقيع التوثيق\n';
txs.forEach(t => {
csv += `"${new Date(t.date).toLocaleString('en-US')}","${(t.desc || '').replace(/"/g, '""')}","${t.type}","${t.amount}","${t.signature || ''}"\n`;
});
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `كشف_حساب_المحفظة_${new Date().toISOString().slice(0, 10)}.csv`;
a.click();
URL.revokeObjectURL(url);
};
}
};
/**
* تهيئة وحدة المحفظة
*/
const initWalletModule = async () => {
const mainContentArea = document.getElementById('s7ihkirolg');
if (!mainContentArea) return false;
buildWalletLayoutElements();
bindWalletTabToggles();
bindConverterAndFilterEvents();
await renderLedgerTable();
await renderTimeCapsulesList();
renderConverterUI();
LedgerState.isInitialized = true;
return true;
};
/**
* تصدير وحدة المحفظة
*/
global.FritreeWallet = {
init: initWalletModule,
signReceipt: signTransactionReceipt,
verifyReceipt: verifyTransactionReceipt,
auditHistory: auditTransactionHistory,
renderLedgerTable,
renderCapsules: renderTimeCapsulesList,
renderConverter: renderConverterUI,
executeSwap: executePointsSwap
};
if (typeof document !== 'undefined') {
if (!initWalletModule()) {
document.addEventListener('DOMContentLoaded', initWalletModule);
window.addEventListener('load', initWalletModule);
}
}
})(typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : this);