2026-05-18 16:32:42 +08:00
|
|
|
import React, { useEffect, useState } from 'react';
|
|
|
|
|
import { useTheme } from '../../contexts';
|
2026-05-14 15:07:34 +08:00
|
|
|
import { Content } from '../../components/layout/Content';
|
|
|
|
|
import { TPattern } from '../../components/common/TPattern';
|
|
|
|
|
import { getSystemStats, getSystemConfig, type SystemStats, type SystemConfig } from '../../api/status';
|
|
|
|
|
import { getDocumentList, type DocInfo } from '../../api/docs';
|
|
|
|
|
|
|
|
|
|
const StatsCard = ({ label, value, accent = false }: {
|
|
|
|
|
label: string;
|
|
|
|
|
value: number;
|
|
|
|
|
accent?: boolean;
|
|
|
|
|
}) => {
|
|
|
|
|
const { theme, isDark } = useTheme();
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div style={{
|
|
|
|
|
padding: 20,
|
|
|
|
|
background: theme.bgCard,
|
|
|
|
|
borderRadius: 12,
|
|
|
|
|
border: `1px solid ${accent ? theme.accent : theme.border}`,
|
|
|
|
|
position: 'relative',
|
|
|
|
|
overflow: 'hidden',
|
|
|
|
|
boxShadow: !isDark ? '0 2px 8px rgba(226,0,116,0.06)' : 'none',
|
|
|
|
|
}}>
|
|
|
|
|
<div style={{
|
|
|
|
|
position: 'absolute',
|
|
|
|
|
top: 0,
|
|
|
|
|
left: 0,
|
|
|
|
|
right: 0,
|
|
|
|
|
height: 3,
|
|
|
|
|
background: theme.gradientAccent,
|
|
|
|
|
}} />
|
|
|
|
|
<div className="mono" style={{ fontSize: 11, color: theme.text3, marginBottom: 8, letterSpacing: '1px' }}>{label}</div>
|
|
|
|
|
<div className="mono" style={{ fontSize: 32, fontWeight: 700, color: accent ? theme.accent : theme.text }}>{value}</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const StatusPage: React.FC = () => {
|
|
|
|
|
const { theme, isDark } = useTheme();
|
2026-05-18 16:32:42 +08:00
|
|
|
const [stats, setStats] = useState<SystemStats>({
|
|
|
|
|
documents_total: 0,
|
|
|
|
|
documents_indexed: 0,
|
|
|
|
|
documents_failed: 0,
|
|
|
|
|
chunks_total: 0,
|
|
|
|
|
});
|
2026-05-14 15:07:34 +08:00
|
|
|
const [config, setConfig] = useState<SystemConfig | null>(null);
|
|
|
|
|
const [docs, setDocs] = useState<DocInfo[]>([]);
|
|
|
|
|
|
2026-05-18 16:32:42 +08:00
|
|
|
async function loadData() {
|
2026-05-14 15:07:34 +08:00
|
|
|
try {
|
|
|
|
|
const [statsRes, configRes, docsRes] = await Promise.all([
|
|
|
|
|
getSystemStats(),
|
|
|
|
|
getSystemConfig(),
|
|
|
|
|
getDocumentList(),
|
|
|
|
|
]);
|
|
|
|
|
setStats(statsRes);
|
|
|
|
|
setConfig(configRes);
|
|
|
|
|
setDocs(docsRes.docs);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Failed to load status data:', error);
|
|
|
|
|
}
|
2026-05-18 16:32:42 +08:00
|
|
|
}
|
2026-05-14 15:07:34 +08:00
|
|
|
|
2026-05-18 16:32:42 +08:00
|
|
|
useEffect(() => {
|
|
|
|
|
const timerId = window.setTimeout(() => {
|
|
|
|
|
void loadData();
|
|
|
|
|
}, 0);
|
|
|
|
|
return () => window.clearTimeout(timerId);
|
|
|
|
|
}, []);
|
2026-05-14 15:07:34 +08:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Content>
|
|
|
|
|
<TPattern />
|
|
|
|
|
<section style={{ marginBottom: 48 }}>
|
|
|
|
|
<div style={{
|
|
|
|
|
display: 'grid',
|
|
|
|
|
gridTemplateColumns: 'repeat(4, 1fr)',
|
|
|
|
|
gap: 16,
|
|
|
|
|
}}>
|
2026-05-18 16:32:42 +08:00
|
|
|
<StatsCard label="DOCUMENTS" value={stats.documents_total} />
|
|
|
|
|
<StatsCard label="INDEXED" value={stats.documents_indexed} />
|
|
|
|
|
<StatsCard label="FAILED" value={stats.documents_failed} />
|
|
|
|
|
<StatsCard label="CHUNKS" value={stats.chunks_total} accent />
|
2026-05-14 15:07:34 +08:00
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section style={{ marginBottom: 48 }}>
|
|
|
|
|
<h2 style={{
|
|
|
|
|
fontSize: 14,
|
|
|
|
|
fontWeight: 600,
|
|
|
|
|
color: theme.accent,
|
|
|
|
|
marginBottom: 20,
|
|
|
|
|
letterSpacing: '1px',
|
|
|
|
|
}}>SYSTEM CONFIGURATION</h2>
|
|
|
|
|
|
|
|
|
|
<div style={{ marginBottom: 20 }}>
|
2026-05-18 16:32:42 +08:00
|
|
|
<div className="mono" style={{ fontSize: 11, color: theme.text3, marginBottom: 12, letterSpacing: '1px' }}>MODELS</div>
|
2026-05-14 15:07:34 +08:00
|
|
|
<div style={{
|
|
|
|
|
display: 'grid',
|
|
|
|
|
gridTemplateColumns: '1fr 1fr',
|
|
|
|
|
gap: 12,
|
|
|
|
|
}}>
|
|
|
|
|
{[
|
2026-05-18 16:32:42 +08:00
|
|
|
['LLM Provider', config?.llm_provider || '-'],
|
|
|
|
|
['LLM Model', config?.llm_model || '-'],
|
|
|
|
|
['Embedding Model', config?.embedding_model || '-'],
|
|
|
|
|
['Embedding Dim', String(config?.embedding_dim || 0)],
|
2026-05-14 15:07:34 +08:00
|
|
|
].map(([k, v]) => (
|
|
|
|
|
<div key={k} style={{
|
|
|
|
|
display: 'flex',
|
|
|
|
|
justifyContent: 'space-between',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
padding: 16,
|
|
|
|
|
background: theme.bgCard,
|
|
|
|
|
borderRadius: 10,
|
|
|
|
|
border: `1px solid ${theme.border}`,
|
|
|
|
|
boxShadow: !isDark ? '0 2px 8px rgba(226,0,116,0.04)' : 'none',
|
|
|
|
|
}}>
|
|
|
|
|
<span className="mono" style={{ fontSize: 12, color: theme.text3 }}>{k}</span>
|
|
|
|
|
<span style={{ fontSize: 14, fontWeight: 500 }}>{v}</span>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div style={{ marginBottom: 20 }}>
|
2026-05-18 16:32:42 +08:00
|
|
|
<div className="mono" style={{ fontSize: 11, color: theme.text3, marginBottom: 12, letterSpacing: '1px' }}>STORAGE AND PATHS</div>
|
2026-05-14 15:07:34 +08:00
|
|
|
<div style={{
|
|
|
|
|
display: 'grid',
|
|
|
|
|
gridTemplateColumns: '1fr 1fr',
|
|
|
|
|
gap: 12,
|
|
|
|
|
}}>
|
|
|
|
|
{[
|
2026-05-18 16:32:42 +08:00
|
|
|
['Milvus Collection', config?.milvus_collection || '-'],
|
|
|
|
|
['Metadata Path', config?.document_metadata_path || '-'],
|
|
|
|
|
['Embedding Base URL', config?.embedding_base_url || '-'],
|
2026-05-14 15:07:34 +08:00
|
|
|
].map(([k, v]) => (
|
|
|
|
|
<div key={k} style={{
|
|
|
|
|
display: 'flex',
|
|
|
|
|
justifyContent: 'space-between',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
padding: 16,
|
|
|
|
|
background: theme.bgCard,
|
|
|
|
|
borderRadius: 10,
|
|
|
|
|
border: `1px solid ${theme.border}`,
|
|
|
|
|
boxShadow: !isDark ? '0 2px 8px rgba(226,0,116,0.04)' : 'none',
|
|
|
|
|
}}>
|
|
|
|
|
<span className="mono" style={{ fontSize: 12, color: theme.text3 }}>{k}</span>
|
|
|
|
|
<span style={{ fontSize: 14, fontWeight: 500 }}>{v}</span>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section>
|
|
|
|
|
<h2 style={{
|
|
|
|
|
fontSize: 14,
|
|
|
|
|
fontWeight: 600,
|
|
|
|
|
color: theme.accent,
|
|
|
|
|
marginBottom: 20,
|
|
|
|
|
letterSpacing: '1px',
|
|
|
|
|
}}>DOCUMENT INDEX</h2>
|
|
|
|
|
{docs.map(d => (
|
|
|
|
|
<div key={d.id} style={{
|
|
|
|
|
display: 'flex',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
justifyContent: 'space-between',
|
|
|
|
|
padding: 16,
|
|
|
|
|
background: theme.bgCard,
|
|
|
|
|
borderRadius: 10,
|
|
|
|
|
marginBottom: 10,
|
|
|
|
|
border: `1px solid ${theme.border}`,
|
|
|
|
|
}}>
|
|
|
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
|
|
|
|
<span style={{ fontSize: 14 }}>{d.name}</span>
|
2026-05-18 16:32:42 +08:00
|
|
|
<span className="mono" style={{ fontSize: 11, color: theme.text3 }}>
|
|
|
|
|
{d.updated_at ? new Date(d.updated_at).toLocaleString() : d.status}
|
|
|
|
|
</span>
|
2026-05-14 15:07:34 +08:00
|
|
|
</div>
|
|
|
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
|
|
|
|
|
<span className="mono" style={{ fontSize: 12, color: theme.text2 }}>{d.chunks} chunks</span>
|
|
|
|
|
<div style={{
|
|
|
|
|
padding: '4px 12px',
|
2026-05-18 16:32:42 +08:00
|
|
|
background: d.status === 'failed' ? '#d64545' : theme.green,
|
2026-05-14 15:07:34 +08:00
|
|
|
borderRadius: 6,
|
|
|
|
|
}}>
|
2026-05-18 16:32:42 +08:00
|
|
|
<span className="mono" style={{ fontSize: 10, fontWeight: 600, color: '#fff' }}>
|
|
|
|
|
{d.status.toUpperCase()}
|
|
|
|
|
</span>
|
2026-05-14 15:07:34 +08:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</section>
|
|
|
|
|
</Content>
|
|
|
|
|
);
|
2026-05-18 16:32:42 +08:00
|
|
|
};
|