This commit is contained in:
ValueOn AG 2026-04-14 08:40:44 +02:00
parent c89d96c6b0
commit 7758f9a58d
2 changed files with 106 additions and 0 deletions

View file

@ -80,3 +80,28 @@
color: #c62828; color: #c62828;
padding: 1rem; padding: 1rem;
} }
.recentTable {
width: 100%;
border-collapse: collapse;
font-size: 0.85rem;
}
.recentTable th {
text-align: left;
font-weight: 600;
color: var(--text-secondary, #666);
padding: 0.5rem 0.75rem;
border-bottom: 2px solid var(--border-color, #e0e0e0);
white-space: nowrap;
}
.recentTable td {
padding: 0.45rem 0.75rem;
border-bottom: 1px solid var(--border-color, #f0f0f0);
color: var(--text-primary, #1a1a1a);
}
.recentTable tbody tr:hover {
background: var(--bg-secondary, #fafafa);
}

View file

@ -41,6 +41,39 @@ function _mimeLabel(key: string, t: (k: string) => string): string {
const CHART_COLORS = ['#1976d2', '#00897b', '#6a1b9a', '#e65100', '#5d4037', '#455a64', '#c62828']; const CHART_COLORS = ['#1976d2', '#00897b', '#6a1b9a', '#e65100', '#5d4037', '#455a64', '#c62828'];
function _formatTimestamp(ts: number | null | undefined): string {
if (ts == null || ts <= 0) return '';
try {
const d = new Date(ts * 1000);
return d.toLocaleString('de-CH', {
day: '2-digit', month: '2-digit', year: 'numeric',
hour: '2-digit', minute: '2-digit',
});
} catch {
return '';
}
}
function _shortMime(mime: string): string {
const m = (mime || '').toLowerCase();
if (m.includes('pdf')) return 'PDF';
if (m.includes('wordprocessing') || m.includes('msword')) return 'Word';
if (m.includes('spreadsheet') || m.includes('excel')) return 'Excel';
if (m.includes('presentation') || m.includes('powerpoint')) return 'PowerPoint';
if (m.startsWith('text/')) return 'Text';
if (m.startsWith('image/')) return 'Bild';
if (m.includes('html')) return 'HTML';
return mime || '';
}
const _STATUS_COLORS: Record<string, string> = {
indexed: '#2e7d32',
extracted: '#1565c0',
embedding: '#6a1b9a',
pending: '#e65100',
failed: '#c62828',
};
interface RagKpis { interface RagKpis {
indexedDocuments: number; indexedDocuments: number;
indexedBytesTotal: number; indexedBytesTotal: number;
@ -51,6 +84,14 @@ interface RagKpis {
workflowEntities: number; workflowEntities: number;
} }
interface RecentlyIndexedDoc {
fileName: string;
mimeType: string;
status: string;
extractedAt: number | null;
totalSize: number;
}
interface RagStatsResponse { interface RagStatsResponse {
error?: string; error?: string;
scope?: { scope?: {
@ -63,6 +104,7 @@ interface RagStatsResponse {
documentsByMimeCategory?: Record<string, number>; documentsByMimeCategory?: Record<string, number>;
chunksByContentType?: Record<string, number>; chunksByContentType?: Record<string, number>;
timelineIndexedDocuments?: Array<{ date: string; indexedDocuments: number }>; timelineIndexedDocuments?: Array<{ date: string; indexedDocuments: number }>;
recentlyIndexedDocuments?: RecentlyIndexedDoc[];
generatedAtUtc?: string; generatedAtUtc?: string;
} }
@ -181,6 +223,45 @@ export const WorkspaceRagInsightsPage: React.FC = () => {
</div> </div>
)} )}
{(stats?.recentlyIndexedDocuments ?? []).length > 0 && (
<div className={styles.chartBlock}>
<h3 className={styles.chartTitle}>{t('Zuletzt indexierte Dokumente')}</h3>
<div style={{ overflowX: 'auto' }}>
<table className={styles.recentTable}>
<thead>
<tr>
<th>{t('Dateiname')}</th>
<th>{t('Format')}</th>
<th>{t('Grösse')}</th>
<th>{t('Status')}</th>
<th>{t('Indexiert am')}</th>
</tr>
</thead>
<tbody>
{(stats?.recentlyIndexedDocuments ?? []).map((doc, i) => (
<tr key={i}>
<td title={doc.fileName} style={{ maxWidth: 280, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{doc.fileName || ''}
</td>
<td>{_shortMime(doc.mimeType)}</td>
<td style={{ whiteSpace: 'nowrap' }}>{formatBinaryDataSizeBytes(doc.totalSize)}</td>
<td>
<span style={{
color: _STATUS_COLORS[doc.status] ?? '#666',
fontWeight: 500,
}}>
{doc.status}
</span>
</td>
<td style={{ whiteSpace: 'nowrap' }}>{_formatTimestamp(doc.extractedAt)}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
<div className={styles.chartBlock}> <div className={styles.chartBlock}>
<h3 className={styles.chartTitle}>{t('Neu indexierte Dokumente pro Tag')}</h3> <h3 className={styles.chartTitle}>{t('Neu indexierte Dokumente pro Tag')}</h3>
{timeline.length === 0 ? ( {timeline.length === 0 ? (