livrare lot 2

This commit is contained in:
EVOTECH IT SRL 2026-07-10 03:39:53 -07:00
commit 8ecc78e729
763 changed files with 164593 additions and 0 deletions

View file

@ -0,0 +1,271 @@
import React from 'react';
import {
Box, Paper, Typography, Chip, IconButton, Tooltip, LinearProgress,
Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TablePagination,
} from '@mui/material';
import {
History as HistoryIcon,
Delete as DeleteIcon,
Visibility as VisibilityIcon,
ContentCopy as CopyIcon,
Facebook as FacebookIcon,
Cancel as CancelIcon,
Replay as ResumeIcon,
} from '@mui/icons-material';
import type { AnalysisSession, PaginationInfo } from './history-types';
import { getRiskIcon, getRiskCategoryColor, formatDuration, formatDate } from './history-helpers';
// Sessions still in flight can be canceled (agent-v3 /:id/cancel).
const CANCELABLE = new Set(['pending', 'running', 'processing']);
// Failed/canceled sessions can be resumed from checkpoint (/:id/resume).
const RESUMABLE = new Set(['failed', 'canceled']);
interface Props {
loading: boolean;
analyses: AnalysisSession[];
pagination: PaginationInfo;
copiedId: string | null;
onPageChange: (event: unknown, newPage: number) => void;
onRowsPerPageChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
onViewDetails: (sessionId: string) => void;
onDeleteClick: (session: AnalysisSession) => void;
onCopyId: (e: React.MouseEvent, id: string) => void;
onSocialPostClick?: (session: AnalysisSession) => void;
onCancelClick?: (session: AnalysisSession) => void;
onResumeClick?: (session: AnalysisSession) => void;
}
export const HistoryTable: React.FC<Props> = ({
loading,
analyses,
pagination,
copiedId,
onPageChange,
onRowsPerPageChange,
onViewDetails,
onDeleteClick,
onCopyId,
onSocialPostClick,
onCancelClick,
onResumeClick,
}) => (
<Paper>
{loading && <LinearProgress />}
<TableContainer>
<Table size="small">
<TableHead>
<TableRow sx={{ bgcolor: 'action.hover' }}>
<TableCell sx={{ fontWeight: 600 }}>Session ID</TableCell>
<TableCell sx={{ fontWeight: 600 }}>Date</TableCell>
<TableCell sx={{ fontWeight: 600 }}>Email</TableCell>
<TableCell sx={{ fontWeight: 600 }}>Source</TableCell>
<TableCell align="center" sx={{ fontWeight: 600 }}>Risk</TableCell>
<TableCell align="center" sx={{ fontWeight: 600 }}>Techniques</TableCell>
<TableCell align="center" sx={{ fontWeight: 600 }}>AI %</TableCell>
<TableCell align="center" sx={{ fontWeight: 600 }}>Claims</TableCell>
<TableCell align="center" sx={{ fontWeight: 600 }}>Duration</TableCell>
<TableCell align="center" sx={{ fontWeight: 600 }}>Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{!loading && analyses.length === 0 ? (
<TableRow>
<TableCell colSpan={10} align="center" sx={{ py: 4 }}>
<HistoryIcon sx={{ fontSize: 48, color: 'text.disabled', mb: 1 }} />
<Typography color="text.secondary">No analyses found</Typography>
</TableCell>
</TableRow>
) : (
analyses.map((analysis) => (
<TableRow
key={analysis.session_id}
hover
sx={{ cursor: 'pointer' }}
onClick={() => onViewDetails(analysis.session_id)}
>
<TableCell onClick={(e) => e.stopPropagation()}>
<Box display="flex" alignItems="center" gap={0.5}>
<Tooltip title={analysis.session_id}>
<Typography variant="caption" sx={{ fontFamily: 'monospace' }}>
{analysis.session_id.substring(0, 8)}...
</Typography>
</Tooltip>
<Tooltip title={copiedId === analysis.session_id ? 'Copied!' : 'Copy'}>
<IconButton size="small" onClick={(e) => onCopyId(e, analysis.session_id)}>
<CopyIcon sx={{ fontSize: 14, color: copiedId === analysis.session_id ? 'success.main' : 'action.active' }} />
</IconButton>
</Tooltip>
</Box>
</TableCell>
<TableCell>
<Typography variant="body2" sx={{ fontWeight: 500 }}>
{formatDate(analysis.started_at)}
</Typography>
<Typography variant="caption" color="text.secondary">
{analysis.input_type}
</Typography>
</TableCell>
<TableCell>
<Tooltip title={`User ID: ${analysis.user_id}`}>
<Typography
variant="body2"
sx={{
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
maxWidth: 180,
}}
>
{analysis.user_email || <span style={{ color: '#999' }}>No email</span>}
</Typography>
</Tooltip>
</TableCell>
<TableCell>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
<Chip
label={analysis.input_type}
size="small"
variant="outlined"
color={analysis.input_type === 'url' ? 'primary' : analysis.input_type === 'text' ? 'default' : 'secondary'}
sx={{ textTransform: 'uppercase', fontSize: 11, height: 22 }}
/>
<Chip label={analysis.source_app} size="small" variant="outlined" sx={{ fontSize: 11, height: 22 }} />
</Box>
{(analysis.source_verdict || analysis.domain) && (
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', mt: 0.3 }}>
{analysis.source_publication || analysis.domain || ''}
{(analysis.source_verdict || analysis.domain_verdict) && (
<Chip
label={analysis.source_verdict || analysis.domain_verdict}
size="small"
sx={{
ml: 0.5, height: 18, fontSize: 10,
bgcolor: (analysis.source_verdict || analysis.domain_verdict) === 'TRUSTED' ? '#22c55e'
: (analysis.source_verdict || analysis.domain_verdict) === 'SUSPICIOUS' ? '#f97316'
: (analysis.source_verdict || analysis.domain_verdict) === 'UNTRUSTED' ? '#ef4444' : undefined,
color: (analysis.source_verdict || analysis.domain_verdict) !== 'NEUTRAL' ? '#fff' : undefined,
}}
/>
)}
</Typography>
)}
</TableCell>
<TableCell align="center">
{analysis.risk_score !== null ? (
<Tooltip title={`${analysis.risk_category} - ${analysis.risk_level}`}>
<Chip
icon={getRiskIcon(analysis.risk_level)}
label={`${analysis.risk_score} ${analysis.risk_category || ''}`}
size="small"
sx={{
bgcolor: getRiskCategoryColor(analysis.risk_category),
color: '#fff',
fontWeight: 600,
'& .MuiChip-icon': { color: '#fff' },
}}
/>
</Tooltip>
) : (
<Chip label="-" size="small" variant="outlined" />
)}
</TableCell>
<TableCell align="center">
{analysis.techniques_count !== null ? (
<Chip
label={analysis.techniques_count}
size="small"
color={Number(analysis.techniques_count) > 0 ? 'warning' : 'default'}
variant="outlined"
/>
) : '-'}
</TableCell>
<TableCell align="center">
{analysis.ai_probability !== null ? (
<Tooltip title={analysis.ai_verdict || ''}>
<Chip
label={`${Math.round(Number(analysis.ai_probability))}%`}
size="small"
color={Number(analysis.ai_probability) > 50 ? 'warning' : 'default'}
variant="outlined"
/>
</Tooltip>
) : '-'}
</TableCell>
<TableCell align="center">
{analysis.total_claims !== null ? (
<Tooltip title={`True: ${analysis.verified_true}, False: ${analysis.verified_false}`}>
<Typography variant="body2">
{analysis.total_claims}
</Typography>
</Tooltip>
) : '-'}
</TableCell>
<TableCell align="center">
<Typography variant="body2" color="text.secondary">
{formatDuration(analysis.total_duration_ms)}
</Typography>
</TableCell>
<TableCell align="center" onClick={(e) => e.stopPropagation()}>
<IconButton
size="small"
onClick={() => onViewDetails(analysis.session_id)}
title="View details"
>
<VisibilityIcon fontSize="small" />
</IconButton>
{onSocialPostClick && (
<IconButton
size="small"
onClick={() => onSocialPostClick(analysis)}
title="Post to Facebook"
sx={{ color: '#1877F2' }}
>
<FacebookIcon fontSize="small" />
</IconButton>
)}
{onCancelClick && CANCELABLE.has(analysis.status) && (
<IconButton
size="small"
onClick={() => onCancelClick(analysis)}
title="Cancel running analysis"
color="warning"
>
<CancelIcon fontSize="small" />
</IconButton>
)}
{onResumeClick && RESUMABLE.has(analysis.status) && (
<IconButton
size="small"
onClick={() => onResumeClick(analysis)}
title="Resume from checkpoint (re-runs only unfinished components)"
color="primary"
>
<ResumeIcon fontSize="small" />
</IconButton>
)}
<IconButton
size="small"
onClick={() => onDeleteClick(analysis)}
title="Delete"
color="error"
>
<DeleteIcon fontSize="small" />
</IconButton>
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
</TableContainer>
<TablePagination
component="div"
count={pagination.total}
page={pagination.page - 1}
onPageChange={onPageChange}
rowsPerPage={pagination.limit}
onRowsPerPageChange={onRowsPerPageChange}
rowsPerPageOptions={[10, 20, 50, 100]}
/>
</Paper>
);