fix: embedding background persistence, polling timeout, Error section hidden
- Embed task_id persisted in Zustand store (survives page navigation) - Resume polling on page mount if active task exists - Polling timeout increased to 30min (was 5min, too short for large sections) - Embed button hidden for Error and Other sections - Shared file-based status for multi-worker visibility
This commit is contained in:
@@ -581,7 +581,9 @@ export default function ReportsPage() {
|
|||||||
const setEmbedAdb = store.setRptEmbedAdb;
|
const setEmbedAdb = store.setRptEmbedAdb;
|
||||||
const embedTable = store.rptEmbedTable;
|
const embedTable = store.rptEmbedTable;
|
||||||
const setEmbedTable = store.setRptEmbedTable;
|
const setEmbedTable = store.setRptEmbedTable;
|
||||||
const [embedLoading, setEmbedLoading] = useState(''); // '' = idle, 'all' = full embed, section name = section embed
|
const embedTaskId = store.rptEmbedTaskId;
|
||||||
|
const setEmbedTaskId = store.setRptEmbedTaskId;
|
||||||
|
const [embedLoading, setEmbedLoading] = useState('');
|
||||||
const [embedMsg, setEmbedMsg] = useState<{ type: 's' | 'e'; text: string } | null>(null);
|
const [embedMsg, setEmbedMsg] = useState<{ type: 's' | 'e'; text: string } | null>(null);
|
||||||
const [embedProgress, setEmbedProgress] = useState<{ inserted: number; skipped: number; processed: number; total: number } | null>(null);
|
const [embedProgress, setEmbedProgress] = useState<{ inserted: number; skipped: number; processed: number; total: number } | null>(null);
|
||||||
|
|
||||||
@@ -782,37 +784,53 @@ export default function ReportsPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const pollEmbeddingStatus = useCallback(async (taskId: string) => {
|
const pollEmbeddingStatus = useCallback((taskId: string) => {
|
||||||
setEmbedMsg({ type: 's', text: 'Embedding iniciado...' });
|
setEmbedMsg({ type: 's', text: 'Embedding em andamento...' });
|
||||||
setEmbedProgress(null);
|
setEmbedTaskId(taskId);
|
||||||
const poll = setInterval(async () => {
|
const poll = setInterval(async () => {
|
||||||
try {
|
try {
|
||||||
const s = await reportsApi.embeddingStatus(taskId);
|
const s = await reportsApi.embeddingStatus(taskId);
|
||||||
if (s.total) {
|
const data = s as unknown as Record<string, unknown>;
|
||||||
setEmbedProgress({
|
const total = (data.total as number) || 0;
|
||||||
inserted: s.inserted || 0,
|
const inserted = (data.inserted as number) || 0;
|
||||||
skipped: (s as unknown as Record<string, number>).skipped || 0,
|
const skipped = (data.skipped as number) || 0;
|
||||||
processed: (s as unknown as Record<string, number>).processed || s.inserted || 0,
|
const processed = (data.processed as number) || inserted;
|
||||||
total: s.total,
|
|
||||||
});
|
if (total > 0) {
|
||||||
|
setEmbedProgress({ inserted, skipped, processed, total });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.status === 'done') {
|
||||||
|
clearInterval(poll);
|
||||||
|
setEmbedProgress({ inserted, skipped, processed: total, total });
|
||||||
|
setEmbedMsg({ type: 's', text: s.message });
|
||||||
|
setTimeout(() => { setEmbedProgress(null); setEmbedLoading(''); setEmbedTaskId(''); }, 3000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (s.status === 'error') {
|
||||||
|
clearInterval(poll);
|
||||||
|
setEmbedMsg({ type: 'e', text: s.message });
|
||||||
|
setTimeout(() => { setEmbedProgress(null); setEmbedLoading(''); setEmbedTaskId(''); }, 3000);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (s.status === 'running') {
|
if (s.status === 'running') {
|
||||||
setEmbedMsg({ type: 's', text: s.message });
|
setEmbedMsg({ type: 's', text: s.message });
|
||||||
} else if (s.status === 'done') {
|
|
||||||
clearInterval(poll);
|
|
||||||
setEmbedMsg({ type: 's', text: s.message });
|
|
||||||
setEmbedProgress(null);
|
|
||||||
setEmbedLoading('');
|
|
||||||
} else if (s.status === 'error') {
|
|
||||||
clearInterval(poll);
|
|
||||||
setEmbedMsg({ type: 'e', text: s.message });
|
|
||||||
setEmbedProgress(null);
|
|
||||||
setEmbedLoading('');
|
|
||||||
}
|
}
|
||||||
} catch { /* keep polling */ }
|
} catch {
|
||||||
|
// Network error — keep polling
|
||||||
|
}
|
||||||
}, 2000);
|
}, 2000);
|
||||||
setTimeout(() => { clearInterval(poll); setEmbedLoading(''); setEmbedProgress(null); }, 300000);
|
// Cleanup on unmount
|
||||||
}, []);
|
return () => clearInterval(poll);
|
||||||
|
}, [setEmbedTaskId]);
|
||||||
|
|
||||||
|
// Resume polling if returning to page with active embed task
|
||||||
|
useEffect(() => {
|
||||||
|
if (!embedTaskId) return;
|
||||||
|
setEmbedLoading('all');
|
||||||
|
const cleanup = pollEmbeddingStatus(embedTaskId);
|
||||||
|
return cleanup;
|
||||||
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
const handleEmbed = async (rid: string) => {
|
const handleEmbed = async (rid: string) => {
|
||||||
const adbId = embedAdb || (adbCfg.length > 0 ? adbCfg[0].id : '');
|
const adbId = embedAdb || (adbCfg.length > 0 ? adbCfg[0].id : '');
|
||||||
@@ -1530,7 +1548,7 @@ export default function ReportsPage() {
|
|||||||
<Icon size={13} style={{ color: 'var(--t3)', opacity: 0.7 }} />
|
<Icon size={13} style={{ color: 'var(--t3)', opacity: 0.7 }} />
|
||||||
<span className="text-[.72rem] font-bold" style={{ color: 'var(--t1)' }}>{sec}</span>
|
<span className="text-[.72rem] font-bold" style={{ color: 'var(--t1)' }}>{sec}</span>
|
||||||
<span className="text-[.6rem]" style={{ color: 'var(--t4)' }}>({secFiles.length})</span>
|
<span className="text-[.6rem]" style={{ color: 'var(--t4)' }}>({secFiles.length})</span>
|
||||||
{adbCfg.length > 0 && secFiles.some((f) => f.file_name.endsWith('.csv')) && (
|
{adbCfg.length > 0 && sec !== 'Error' && sec !== 'Other' && secFiles.some((f) => f.file_name.endsWith('.csv')) && (
|
||||||
<button
|
<button
|
||||||
onClick={() => handleEmbedSection(sec, secFiles)}
|
onClick={() => handleEmbedSection(sec, secFiles)}
|
||||||
disabled={!!embedLoading}
|
disabled={!!embedLoading}
|
||||||
|
|||||||
@@ -222,6 +222,8 @@ interface AppState {
|
|||||||
rptShowCompliance: boolean;
|
rptShowCompliance: boolean;
|
||||||
rptEmbedAdb: string;
|
rptEmbedAdb: string;
|
||||||
rptEmbedTable: string;
|
rptEmbedTable: string;
|
||||||
|
rptEmbedTaskId: string;
|
||||||
|
setRptEmbedTaskId: (v: string) => void;
|
||||||
setRptOciVal: (v: string) => void;
|
setRptOciVal: (v: string) => void;
|
||||||
setRptLevel: (v: 1 | 2) => void;
|
setRptLevel: (v: 1 | 2) => void;
|
||||||
setRptSelectedRegions: (v: Updater<string[]>) => void;
|
setRptSelectedRegions: (v: Updater<string[]>) => void;
|
||||||
@@ -353,6 +355,8 @@ export const useAppStore = create<AppState>((set) => ({
|
|||||||
rptShowCompliance: false,
|
rptShowCompliance: false,
|
||||||
rptEmbedAdb: '',
|
rptEmbedAdb: '',
|
||||||
rptEmbedTable: '',
|
rptEmbedTable: '',
|
||||||
|
rptEmbedTaskId: '',
|
||||||
|
setRptEmbedTaskId: (v) => set({ rptEmbedTaskId: v }),
|
||||||
setRptOciVal: (v) => set({ rptOciVal: v }),
|
setRptOciVal: (v) => set({ rptOciVal: v }),
|
||||||
setRptLevel: (v) => set({ rptLevel: v }),
|
setRptLevel: (v) => set({ rptLevel: v }),
|
||||||
setRptSelectedRegions: (v) => set((s) => ({ rptSelectedRegions: typeof v === 'function' ? v(s.rptSelectedRegions) : v })),
|
setRptSelectedRegions: (v) => set((s) => ({ rptSelectedRegions: typeof v === 'function' ? v(s.rptSelectedRegions) : v })),
|
||||||
|
|||||||
Reference in New Issue
Block a user