- Surse complete web (React/Vite) + mobil (React Native/Expo) + extensie (MV3) - Documentație de livrare: ghid utilizare, matrice trasabilitate cerințe, raport testare furnizor - Artefacte binare: imagine Docker didi-frontend:lot3-1.0, APK, extensie v3.2.6 + SHA256SUMS - Configurare adresă platformă externalizată (build args / .env / config.js) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
131 lines
5.2 KiB
JavaScript
131 lines
5.2 KiB
JavaScript
// DIDI History Page Script (Full V3 API)
|
|
console.log('[DIDI History] Script loaded');
|
|
|
|
let historyData = [];
|
|
|
|
function getRiskColorByScore(score) {
|
|
if (score >= 80) return '#B91C1C';
|
|
if (score >= 60) return '#EF4444';
|
|
if (score >= 40) return '#F97316';
|
|
if (score >= 20) return '#EAB308';
|
|
return '#22C55E';
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', async () => {
|
|
chrome.runtime.sendMessage({ action: 'getAuthStatus' }, (authResponse) => {
|
|
const authInfo = document.getElementById('authInfo');
|
|
if (authResponse?.authenticated) {
|
|
authInfo.innerHTML = `<span class="auth-connected">Connected</span>`;
|
|
} else {
|
|
authInfo.innerHTML = `<span class="auth-disconnected">Not connected</span>`;
|
|
}
|
|
});
|
|
|
|
chrome.storage.local.get(['didiHistory'], (data) => {
|
|
historyData = data.didiHistory || [];
|
|
console.log('[DIDI History] Loaded', historyData.length, 'items');
|
|
renderHistory();
|
|
});
|
|
|
|
setupEventListeners();
|
|
});
|
|
|
|
function renderHistory() {
|
|
const grid = document.getElementById('historyGrid');
|
|
const subtitle = document.getElementById('subtitle');
|
|
|
|
if (!historyData || historyData.length === 0) {
|
|
subtitle.textContent = 'No analyses yet';
|
|
grid.innerHTML = `<div class="empty-state"><h3>No Analysis History</h3><p>Take a screenshot or analyze text to see results here.</p></div>`;
|
|
return;
|
|
}
|
|
|
|
subtitle.textContent = `${historyData.length} analysis result(s)`;
|
|
|
|
grid.innerHTML = historyData.map((item, index) => {
|
|
const analysis = item.analysis || {};
|
|
// Backend schema is FLAT: components live directly on `analysis`, not nested under `components`.
|
|
const verdict = analysis.verdict || {};
|
|
const techData = analysis.techniques || {};
|
|
const techniques = techData.techniques_detected || [];
|
|
const claimsData = analysis.claims || {};
|
|
const aiData = analysis.ai_tampered || {};
|
|
const riskScore = verdict.risk_score ?? analysis.risk_score ?? 0;
|
|
const riskCategory = verdict.risk_category || analysis.risk_category || 'Unknown';
|
|
const riskColor = getRiskColorByScore(riskScore);
|
|
const aiProb = aiData.ai_probability ?? 0;
|
|
const analysisType = analysis.analysis_type || analysis.input_type || 'text';
|
|
const createdAt = item.timestamp || '';
|
|
|
|
return `
|
|
<div class="history-item" data-index="${index}">
|
|
<div class="risk-badge" style="background: ${riskColor}20; border-color: ${riskColor};">
|
|
<span class="risk-score" style="color: ${riskColor};">${riskScore}</span>
|
|
<span class="risk-category" style="color: ${riskColor};">${riskCategory}</span>
|
|
</div>
|
|
<div class="item-preview">
|
|
<span style="color: #1fb6bd; font-size: 11px; text-transform: uppercase; font-weight: 600;">${analysisType}</span>
|
|
</div>
|
|
<div class="item-stats">
|
|
<div class="stat"><span class="stat-label">Techniques</span><span class="stat-value">${techniques.length}</span></div>
|
|
<div class="stat"><span class="stat-label">Claims</span><span class="stat-value">${claimsData.total_claims || 0}</span></div>
|
|
<div class="stat"><span class="stat-label">AI Prob.</span><span class="stat-value">${aiProb}%</span></div>
|
|
</div>
|
|
<div class="item-actions">
|
|
<button class="view-details-btn" data-index="${index}">View Details</button>
|
|
<button class="delete-btn" data-index="${index}">Delete</button>
|
|
</div>
|
|
${createdAt ? `<div class="timestamp">${new Date(createdAt).toLocaleString()}</div>` : ''}
|
|
</div>`;
|
|
}).join('');
|
|
}
|
|
|
|
function setupEventListeners() {
|
|
const modal = document.getElementById('analysisModal');
|
|
const modalClose = document.getElementById('modalClose');
|
|
|
|
document.getElementById('historyGrid').addEventListener('click', (e) => {
|
|
if (e.target.classList.contains('view-details-btn')) {
|
|
e.preventDefault(); e.stopPropagation();
|
|
openModal(parseInt(e.target.getAttribute('data-index'), 10));
|
|
return;
|
|
}
|
|
if (e.target.classList.contains('delete-btn')) {
|
|
e.preventDefault(); e.stopPropagation();
|
|
deleteItem(parseInt(e.target.getAttribute('data-index'), 10));
|
|
return;
|
|
}
|
|
const card = e.target.closest('.history-item');
|
|
if (card && !e.target.closest('button')) {
|
|
openModal(parseInt(card.getAttribute('data-index'), 10));
|
|
}
|
|
});
|
|
|
|
modalClose.addEventListener('click', () => closeModal());
|
|
modal.addEventListener('click', (e) => { if (e.target === modal) closeModal(); });
|
|
document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeModal(); });
|
|
}
|
|
|
|
function deleteItem(index) {
|
|
if (!confirm('Delete this analysis result?')) return;
|
|
historyData.splice(index, 1);
|
|
chrome.storage.local.set({ didiHistory: historyData }, () => renderHistory());
|
|
}
|
|
|
|
function openModal(index) {
|
|
const item = historyData[index];
|
|
if (!item) return;
|
|
const modal = document.getElementById('analysisModal');
|
|
document.getElementById('modalBody').innerHTML = generateAnalysisHTML(item);
|
|
modal.classList.add('active');
|
|
}
|
|
|
|
function closeModal() {
|
|
document.getElementById('analysisModal').classList.remove('active');
|
|
}
|
|
|
|
function generateAnalysisHTML(item) {
|
|
// Shared renderer mirrors web app PipelineAnalysis layout.
|
|
return window.DidiRender.renderAnalysisHTML(item.analysis || {});
|
|
}
|
|
|