Livrare Lot 3 (Frontend): aplicație web, aplicație mobilă Android, extensie browser
- 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>
This commit is contained in:
commit
cec967f953
321 changed files with 80506 additions and 0 deletions
131
extension/history.js
Normal file
131
extension/history.js
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
// 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 || {});
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue