fc / rotation-performance-ui.js
Althnayi's picture
Upload 56 files
cca0cf3 verified
Raw
History Blame Contribute Delete
24.4 kB
/**
* @fileoverview Rotation Posts Performance Dashboard & Variant Trace Modal
* @module ui/rotation-performance-ui
* @description واجهة قياس كفاءة المنشورات، نسب النجاح في الجروبات، ونافذة تتبع سجل الإرسال التفصيلي.
*/
(global => {
'use strict';
const state = global.FritreeRotationState;
let currentMetricsPage = 1;
const METRICS_ITEMS_PER_PAGE = 8;
/**
* بناء الهيكل البصري لقسم تقييم أداء المنشورات
*/
const buildPerformanceLayout = () => {
const page = document.getElementById('xpoqh5yufi');
if (!page) return;
if (page.querySelector('#fj8dnuc75c')) return;
page.innerHTML = `
<div id="fj8dnuc75c" class="zcjbe1otmg" style="display: flex; flex-direction: column; gap: var(--space-md); direction: rtl; text-align: right; width: 100%; box-sizing: border-box;">
<!-- بطاقات مؤشرات الأداء الرئيسية KPIs -->
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(clamp(130px, 12vw, 200px), 1fr)); gap: 10px; width: 100%;">
<div class="r6wq5imop6" style="padding: clamp(12px, 1.2vw, 16px); border: 1px solid var(--border); flex-direction: row; justify-content: space-between;">
<div style="text-align: right;">
<div class="h7ren96m5v" style="font-size: 10.5px; margin-bottom: 2px;">
<i class="fa-solid fa-play" style="color: var(--primary);"></i> <span>إجمالي النشر</span>
</div>
<strong id="ylohpr45dl" class="kisn3ao2tz" style="font-size: 20px; font-family: var(--font-code); line-height: 1;">
0
</strong>
</div>
<div style="width: 36px; height: 36px; border-radius: var(--radius-md); background: var(--primary-light); color: var(--primary); display: flex; align-items: center; justify-content: center; font-size: 15px; flex-shrink: 0;">
<i class="fa-solid fa-play"></i>
</div>
</div>
<div class="r6wq5imop6" style="padding: clamp(12px, 1.2vw, 16px); border: 1px solid var(--border); flex-direction: row; justify-content: space-between;">
<div style="text-align: right;">
<div class="h7ren96m5v" style="font-size: 10.5px; margin-bottom: 2px;">
<i class="fa-solid fa-circle-check" style="color: var(--success);"></i> <span>منشورات ناجحة</span>
</div>
<strong id="md8oymcc5c" class="kisn3ao2tz" style="font-size: 20px; font-family: var(--font-code); color: var(--success); line-height: 1;">
0
</strong>
</div>
<div style="width: 36px; height: 36px; border-radius: var(--radius-md); background: var(--success-light); color: var(--success); display: flex; align-items: center; justify-content: center; font-size: 15px; flex-shrink: 0;">
<i class="fa-solid fa-circle-check"></i>
</div>
</div>
<div class="r6wq5imop6" style="padding: clamp(12px, 1.2vw, 16px); border: 1px solid var(--border); flex-direction: row; justify-content: space-between;">
<div style="text-align: right;">
<div class="h7ren96m5v" style="font-size: 10.5px; margin-bottom: 2px;">
<i class="fa-solid fa-hourglass-half" style="color: var(--warning);"></i> <span>معلقة للمراجعة</span>
</div>
<strong id="qoumr7z4ii" class="kisn3ao2tz" style="font-size: 20px; font-family: var(--font-code); color: var(--warning); line-height: 1;">
0
</strong>
</div>
<div style="width: 36px; height: 36px; border-radius: var(--radius-md); background: var(--warning-light); color: var(--warning); display: flex; align-items: center; justify-content: center; font-size: 15px; flex-shrink: 0;">
<i class="fa-solid fa-hourglass-half"></i>
</div>
</div>
<div class="r6wq5imop6" style="padding: clamp(12px, 1.2vw, 16px); border: 1px solid var(--border); flex-direction: row; justify-content: space-between;">
<div style="text-align: right;">
<div class="h7ren96m5v" style="font-size: 10.5px; margin-bottom: 2px;">
<i class="fa-solid fa-triangle-exclamation" style="color: var(--danger);"></i> <span>محاولات فشلت</span>
</div>
<strong id="s8t8cbmfne" class="kisn3ao2tz" style="font-size: 20px; font-family: var(--font-code); color: var(--danger); line-height: 1;">
0
</strong>
</div>
<div style="width: 36px; height: 36px; border-radius: var(--radius-md); background: var(--danger-light); color: var(--danger); display: flex; align-items: center; justify-content: center; font-size: 15px; flex-shrink: 0;">
<i class="fa-solid fa-triangle-exclamation"></i>
</div>
</div>
</div>
<!-- جدول تقييم الأداء التفصيلي -->
<div class="d5tp7way8h" style="margin: 0; padding: clamp(12px, 1.4vw, 18px); border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--bg-card); box-sizing: border-box;">
<div class="cm41ch7urz" style="margin-bottom: 10px; border-bottom: 1px solid var(--border); padding-bottom: 8px; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 6px;">
<span style="font-size: 13.5px; font-weight: 800; display: flex; align-items: center; gap: 6px; color: var(--text-main);">
<i class="fa-solid fa-chart-simple" style="color: var(--primary);"></i>
<span>قياس أداء وكفاءة منشورات التدوير</span>
</span>
<span style="font-size: 10.5px; font-weight: 600; color: var(--text-muted);">
<i class="fa-solid fa-hand-pointer"></i> <span>اضغط على أي منشور لعرض سجل إرساله التفصيلي</span>
</span>
</div>
<div class="diktqr0h64" style="max-height: 440px;">
<table style="width: 100%; border-collapse: collapse; direction: rtl; text-align: right;">
<thead>
<tr>
<th style="width: 40%; padding: 10px 12px; text-align: right;"><i class="fa-solid fa-file-signature"></i> <span>عنوان المنشور</span></th>
<th style="width: 15%; padding: 10px; text-align: center;"><i class="fa-solid fa-play"></i> <span>مرات التشغيل</span></th>
<th style="width: 15%; padding: 10px; text-align: center;"><i class="fa-solid fa-circle-check"></i> <span>ناجح</span></th>
<th style="width: 15%; padding: 10px; text-align: center;"><i class="fa-solid fa-hourglass-half"></i> <span>معلق</span></th>
<th style="width: 15%; padding: 10px; text-align: center;"><i class="fa-solid fa-triangle-exclamation"></i> <span>فشل</span></th>
</tr>
</thead>
<tbody id="i4jg5y3ylx"></tbody>
</table>
</div>
<div id="du4l2rjkr6" style="display: flex; justify-content: center; margin-top: 14px; align-items: center; gap: 6px; flex-wrap: wrap;"></div>
</div>
</div>
`;
};
/**
* بناء نافذة تتبع سجل الإرسال للمنشور (Variant Trace Modal)
*/
const appendTraceModalLayout = () => {
let modal = document.getElementById('sg405ywfqm');
if (modal) return;
modal = document.createElement('div');
modal.className = 'cpzxome5dd';
modal.id = 'sg405ywfqm';
modal.style.cssText = 'position: fixed; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(8px); display: none; align-items: center; justify-content: center; z-index: var(--z-modal-backdrop); background-color: rgba(15, 23, 42, 0.65); direction: rtl; text-align: right; box-sizing: border-box;';
modal.innerHTML = `
<div class="s4yvtq9394" style="max-width: min(850px, 94vw); max-height: min(88vh, 850px); border-radius: var(--radius-xl); border: 1px solid var(--border); background: var(--bg-card); box-shadow: var(--shadow-modal);">
<div class="x8pdlyq70a" style="border-bottom: 1px solid var(--border); padding: 14px 20px; display: flex; justify-content: space-between; align-items: center; background: var(--bg-subtle);">
<div class="tooau3ykdi" id="ka8vx2wbd1" style="font-weight: 800; font-size: 14px; display: flex; align-items: center; gap: 8px;">
<i class="fa-solid fa-receipt" style="color: var(--primary);"></i> <span>سجل تتبع الإرسال والنشر للمنشور</span>
</div>
<button class="bcuz086hu4" id="c8r5l8id1i">&times;</button>
</div>
<div class="zn6t0dl6ke" style="padding: clamp(14px, 1.5vw, 20px);">
<div class="diktqr0h64" style="max-height: 380px;">
<table style="width: 100%; border-collapse: collapse; direction: rtl; text-align: right;">
<thead>
<tr>
<th style="padding: 10px 12px;"><i class="fa-solid fa-bullseye"></i> <span>اسم الحملة</span></th>
<th style="padding: 10px 12px;"><i class="fa-solid fa-users"></i> <span>الوجهة المستهدفة</span></th>
<th style="padding: 10px 12px; text-align: center;"><i class="fa-solid fa-circle-info"></i> <span>الحالة</span></th>
<th style="padding: 10px 12px; text-align: center;"><i class="fa-solid fa-link"></i> <span>الرابط / الخطأ</span></th>
</tr>
</thead>
<tbody id="yx004dvp1r"></tbody>
</table>
</div>
</div>
</div>
`;
document.body.appendChild(modal);
document.getElementById('c8r5l8id1i').onclick = () => {
modal.style.display = 'none';
};
};
/**
* رسم وتحديث لوحة تقييم كفاءة المنشورات
*/
const renderEvaluationDashboard = async () => {
const container = document.getElementById('i4jg5y3ylx');
const paginationContainer = document.getElementById('du4l2rjkr6');
if (!container) return;
container.innerHTML = '';
const campaignHistoryData = await global.FritreeStorage.get('campaignHistoryData', []);
const templateStatsMap = {};
campaignHistoryData.forEach(camp => {
if (camp.logs && Array.isArray(camp.logs)) {
camp.logs.forEach(log => {
const templateId = log.variantName;
if (templateId) {
if (!templateStatsMap[templateId]) {
templateStatsMap[templateId] = { usage: 0, success: 0, pending: 0, failed: 0 };
}
templateStatsMap[templateId].usage++;
if (log.status === 'success') templateStatsMap[templateId].success++;
else if (log.status === 'pending') templateStatsMap[templateId].pending++;
else templateStatsMap[templateId].failed++;
}
});
}
});
state.previousPostsData.forEach(p => {
const stats = templateStatsMap[p.id] || { usage: 0, success: 0, pending: 0, failed: 0 };
p.stats = stats;
p.usageCount = stats.usage;
});
if (global.FritreeRotationStore && typeof global.FritreeRotationStore.recalculateScores === 'function') {
global.FritreeRotationStore.recalculateScores();
}
const sorted = [...state.previousPostsData].sort((a, b) => (b.score || 0) - (a.score || 0));
if (state.previousPostsData.length === 0) {
container.innerHTML = '<tr><td colspan="5" style="text-align:center; padding:30px 15px; color:var(--text-muted); font-size:11.5px;"><i class="fa-solid fa-circle-exclamation"></i> <span>لا توجد منشورات مسجلة في مكتبة التدوير حالياً.</span></td></tr>';
if (paginationContainer) paginationContainer.innerHTML = '';
updateEvaluationTotalsRow(sorted);
return;
}
const startIndex = (currentMetricsPage - 1) * METRICS_ITEMS_PER_PAGE;
const pageItems = sorted.slice(startIndex, startIndex + METRICS_ITEMS_PER_PAGE);
pageItems.forEach(p => {
const tr = document.createElement('tr');
tr.style.cssText = 'cursor: pointer; transition: background 0.15s; border-bottom: 1px solid var(--border);';
tr.onclick = () => openVariantTraceModal(p.id);
const tdTitle = document.createElement('td');
tdTitle.style.cssText = 'padding: 10px 12px; font-weight: 700;';
tdTitle.innerHTML = `<i class="fa-solid fa-file-lines" style="color:#0d9488; margin-left:6px;"></i> <span style="color:var(--text-main);">${p.title || (p.text ? p.text.substring(0, 30) + '...' : 'منشور مؤرشف')}</span>`;
const tdUsage = document.createElement('td');
tdUsage.style.cssText = 'padding: 10px; text-align: center; font-weight: 700; font-family: var(--font-code); color: var(--text-main);';
tdUsage.textContent = (p.stats ? p.stats.usage : p.usageCount || 0).toLocaleString('en-US');
const tdSuccess = document.createElement('td');
tdSuccess.style.cssText = 'padding: 10px; text-align: center; font-weight: 700; font-family: var(--font-code); color: var(--success);';
tdSuccess.textContent = (p.stats ? p.stats.success : 0).toLocaleString('en-US');
const tdPending = document.createElement('td');
tdPending.style.cssText = 'padding: 10px; text-align: center; font-weight: 700; font-family: var(--font-code); color: var(--warning);';
tdPending.textContent = (p.stats ? p.stats.pending : 0).toLocaleString('en-US');
const tdFailed = document.createElement('td');
tdFailed.style.cssText = 'padding: 10px; text-align: center; font-weight: 700; font-family: var(--font-code); color: var(--danger);';
tdFailed.textContent = (p.stats ? p.stats.failed : 0).toLocaleString('en-US');
tr.appendChild(tdTitle);
tr.appendChild(tdUsage);
tr.appendChild(tdSuccess);
tr.appendChild(tdPending);
tr.appendChild(tdFailed);
container.appendChild(tr);
});
updateEvaluationTotalsRow(sorted);
renderEvaluationPagination(sorted.length, paginationContainer);
};
/**
* تحديث عدادات إجمالي الأداء
*/
const updateEvaluationTotalsRow = (items) => {
let publishedSum = 0;
let successfulSum = 0;
let reviewedSum = 0;
let failedSum = 0;
items.forEach(p => {
if (p.stats) {
publishedSum += (p.stats.usage || 0);
successfulSum += (p.stats.success || 0);
reviewedSum += (p.stats.pending || 0);
failedSum += (p.stats.failed || 0);
}
});
const totalPubEl = document.getElementById('ylohpr45dl');
const totalSuccEl = document.getElementById('md8oymcc5c');
const totalRevEl = document.getElementById('qoumr7z4ii');
const totalFailEl = document.getElementById('s8t8cbmfne');
if (totalPubEl) totalPubEl.textContent = publishedSum.toLocaleString('en-US');
if (totalSuccEl) totalSuccEl.textContent = successfulSum.toLocaleString('en-US');
if (totalRevEl) totalRevEl.textContent = reviewedSum.toLocaleString('en-US');
if (totalFailEl) totalFailEl.textContent = failedSum.toLocaleString('en-US');
};
/**
* رسم أزرار ترقيم الصفحات
*/
const renderEvaluationPagination = (totalItems, container) => {
if (!container) return;
container.innerHTML = '';
const totalPages = Math.ceil(totalItems / METRICS_ITEMS_PER_PAGE);
if (totalPages <= 1) return;
const prevBtn = document.createElement('button');
prevBtn.className = 'ipxi2jz4g0';
prevBtn.style.cssText = 'padding: 4px 8px; font-size: 10.5px; min-height: 28px;';
prevBtn.innerHTML = '<i class="fa-solid fa-chevron-right"></i> ';
prevBtn.disabled = currentMetricsPage === 1;
prevBtn.onclick = () => {
if (currentMetricsPage > 1) {
currentMetricsPage--;
renderEvaluationDashboard();
}
};
const pageIndicator = document.createElement('span');
pageIndicator.style.cssText = 'font-weight: 700; font-size: 11px; color: var(--text-muted); font-family: var(--font-code); margin: 0 6px;';
pageIndicator.textContent = `صفحة ${currentMetricsPage.toLocaleString('en-US')} من ${totalPages.toLocaleString('en-US')}`;
const nextBtn = document.createElement('button');
nextBtn.className = 'ipxi2jz4g0';
nextBtn.style.cssText = 'padding: 4px 8px; font-size: 10.5px; min-height: 28px;';
nextBtn.innerHTML = '<i class="fa-solid fa-chevron-left"></i> ';
nextBtn.disabled = currentMetricsPage === totalPages;
nextBtn.onclick = () => {
if (currentMetricsPage < totalPages) {
currentMetricsPage++;
renderEvaluationDashboard();
}
};
container.appendChild(prevBtn);
container.appendChild(pageIndicator);
container.appendChild(nextBtn);
};
/**
* فتح نافذة تتبع سجل المنشور
* @param {string} postId
*/
const openVariantTraceModal = async (postId) => {
const tbody = document.getElementById('yx004dvp1r');
const metaInfo = document.getElementById('ka8vx2wbd1');
if (!tbody || !metaInfo) return;
tbody.innerHTML = '';
const post = state.previousPostsData.find(p => p.id === postId);
if (!post) return;
appendTraceModalLayout();
metaInfo.innerHTML = `<i class="fa-solid fa-receipt" style="color:var(--primary);"></i> <span>سجل تشغيل: "${post.title}" | الكفاءة: ${(post.score || 0).toLocaleString('en-US')}%</span>`;
const campaignHistoryData = await global.FritreeStorage.get('campaignHistoryData', []);
let matchCount = 0;
campaignHistoryData.forEach(camp => {
if (camp.logs && Array.isArray(camp.logs)) {
camp.logs.forEach(log => {
if (log.variantName === postId) {
matchCount++;
const tr = document.createElement('tr');
const tdCamp = document.createElement('td');
tdCamp.style.padding = '8px 10px';
tdCamp.style.fontWeight = "700";
tdCamp.textContent = camp.name;
const tdTarget = document.createElement('td');
tdTarget.style.padding = '8px 10px';
tdTarget.textContent = log.groupId || "وجهة غير معروفة";
const tdStatus = document.createElement('td');
tdStatus.style.padding = '8px 10px';
tdStatus.style.textAlign = 'center';
const badge = document.createElement('span');
badge.className = `go30y9mfgh ${log.status === 'success' ? 'success' : (log.status === 'pending' ? 'pending' : 'failed')}`;
badge.textContent = log.status === 'success' ? 'تم النشر' : (log.status === 'pending' ? 'قيد المراجعة' : 'فشل');
tdStatus.appendChild(badge);
const tdLink = document.createElement('td');
tdLink.style.padding = '8px 10px';
tdLink.style.textAlign = 'center';
if (log.postUrl) {
const link = document.createElement('a');
link.href = log.postUrl;
link.target = '_blank';
link.style.cssText = 'font-weight: 700; color: var(--primary); text-decoration: none; display: inline-flex; align-items: center; gap: 4px;';
link.innerHTML = '<i class="fa-solid fa-arrow-up-right-from-square"></i> <span>عرض المنشور</span>';
tdLink.appendChild(link);
} else {
tdLink.innerHTML = `<span style="color:var(--danger); font-size:10px;">${log.error || 'غير متوفر'}</span>`;
}
tr.appendChild(tdCamp);
tr.appendChild(tdTarget);
tr.appendChild(tdStatus);
tr.appendChild(tdLink);
tbody.appendChild(tr);
}
});
}
});
if (matchCount === 0) {
tbody.innerHTML = '<tr><td colspan="4" style="text-align:center; color:var(--text-muted); padding:20px; font-size:11.5px;"><i class="fa-solid fa-folder-open"></i> <span>لا توجد سجلات إرسال لهذا المنشور حتى الآن.</span></td></tr>';
}
const modal = document.getElementById('sg405ywfqm');
if (modal) modal.style.display = 'flex';
};
/**
* تهيئة وحدة تقييم الأداء
*/
const initPerformanceModule = () => {
const page = document.getElementById('xpoqh5yufi');
if (!page) return false;
buildPerformanceLayout();
appendTraceModalLayout();
renderEvaluationDashboard();
return true;
};
/**
* تصدير وحدة واجهة تقييم أداء المنشورات
*/
global.FritreeRotationPerformance = {
init: initPerformanceModule,
renderDashboard: renderEvaluationDashboard,
openTraceModal: openVariantTraceModal
};
})(typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : this);