Show all open (unactioned) items by default, not just the last scan
The default results view loaded only the latest scan session (±300s window), so items dropped out of sight once a newer scan started — and a long scheduled scan could show little or nothing on browser open. Add get_open_items(): every flagged item with no disposition (or status 'unreviewed') across all scans, deduped by id to the latest finished scan. GET /api/db/flagged now serves it when no ?ref is given; ?ref=N still loads a specific past session. Frontend loadHistorySession(null) routes to a new loadOpenItems() loader. Rename the banner button to "Open items" (da/de/en). get_session_items() default is unchanged — export.py and scan_scheduler.py still rely on latest-session for the current scan's report/email. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
67f66c8441
commit
68076eba52
10 files changed
+136
-17
No files matched your search
+2
-1
@@ -40,7 +40,8 @@ Never revert to `!!window._googleConnected` / `_fileSources.length > 0` — thos
|
||||
|
||||
## Scan history browser — history.js + results.js
|
||||
|
||||
- **`S._historyRefScanId`** — `null` = live/SSE mode; positive int = viewing a past session. Set by `loadHistorySession()`; cleared by `exitHistoryMode()`.
|
||||
- **`S._historyRefScanId`** — `null` = live/SSE mode **or** the default open-items view; positive int = viewing a past session. Set by `loadHistorySession()`; cleared by `exitHistoryMode()`.
|
||||
- **`loadHistorySession(null)` → `loadOpenItems()`** — passing `null` no longer resolves to the latest session. It now loads **all open (unactioned) items across every scan** via `GET /api/db/flagged` (no `ref`), leaves `_historyRefScanId` null, and shows no history banner. The "Open items" banner button (`onclick="loadHistorySession(null)"`, key `history_btn_latest`) therefore returns to this open-items view. Specific sessions are still loaded with a positive `ref`, which keeps the re-scan resolved-diff. Do not revert `null` to "resolve latest ref" — that reintroduces the "only the last scan is shown" complaint.
|
||||
- **Auto-load on page load** — `_sseWatchdog()` in `results.js` calls `window.loadHistorySession?.(null)` whenever `/api/scan/status` reports neither `running` (M365 + file lock) nor `google_running` (Google lock) **and** nothing is shown yet (`!S._historyRefScanId && !S.flaggedData.length`). This is **not one-shot** — it retries on every 4s poll until a session is restored, because (a) the replay buffer is empty after a server restart so `sse_replay_done` never fires, and (b) a completed scan's replayed `scan_phase` can leave a running flag set that would otherwise block the load forever. Because both locks are confirmed free, the watchdog clears the stale `_m365/_google/_fileScanRunning` flags before calling. Do not revert to a one-shot `_initialStatusChecked` gate — that reintroduces the "blank grid after refresh/restart" bug. `/api/scan/status` **must** report `google_running` separately; `running` alone misses live Google scans. The `sse_replay_done` handler in `scan.js` still retries for the non-empty-buffer (no-restart) case.
|
||||
- **History banner** (`#historyBanner`) — shown when `S._historyRefScanId` is set. Do not hide/show from outside `history.js`.
|
||||
- **Session picker** (`#historyDropdown`) — rendered inside `[data-history-wrap]` so the outside-click handler works correctly. Do not move the picker outside this wrapper.
|
||||
|
||||
+39
-9
@@ -38,20 +38,50 @@ function invalidateHistoryCache() {
|
||||
|
||||
// ── Load a session into the results grid ──────────────────────────────────────
|
||||
|
||||
async function loadHistorySession(refScanId) {
|
||||
// refScanId: null → latest session, positive int → specific session
|
||||
let resolvedRef = refScanId;
|
||||
if (resolvedRef === null) {
|
||||
const sessions = _sessions !== null ? _sessions : await _fetchSessions();
|
||||
// Bail if a scan started while we were fetching sessions
|
||||
// Default landing view: every flagged item still awaiting action, across all
|
||||
// scans (not just the latest session). Leaves S._historyRefScanId null (live
|
||||
// mode) and shows no history banner — this is "now", not a past session.
|
||||
async function loadOpenItems() {
|
||||
// Bail if a scan is running — live SSE owns the grid then.
|
||||
if (S._m365ScanRunning || S._googleScanRunning || S._fileScanRunning) return;
|
||||
try {
|
||||
const r = await fetch('/api/db/flagged');
|
||||
const items = await r.json();
|
||||
if (S._m365ScanRunning || S._googleScanRunning || S._fileScanRunning) return;
|
||||
if (!sessions.length) {
|
||||
// No scans in DB — nothing to show
|
||||
closeHistoryPicker();
|
||||
|
||||
if (!Array.isArray(items) || items.length === 0) {
|
||||
S._historyRefScanId = null;
|
||||
_setHistoryBanner(false);
|
||||
window.loadLastScanSummary?.();
|
||||
return;
|
||||
}
|
||||
resolvedRef = sessions[0].ref_scan_id;
|
||||
|
||||
S._historyRefScanId = null;
|
||||
S.flaggedData = items;
|
||||
S.filteredData = [];
|
||||
|
||||
const grid = document.getElementById('grid');
|
||||
const emptyState = document.getElementById('emptyState');
|
||||
const lastScan = document.getElementById('lastScanSummary');
|
||||
if (emptyState) emptyState.style.display = 'none';
|
||||
if (lastScan) lastScan.style.display = 'none';
|
||||
if (grid) { grid.innerHTML = ''; grid.style.display = 'grid'; }
|
||||
|
||||
window.renderGrid(items);
|
||||
try { window.markOverdueCards(); } catch(_) {}
|
||||
try { window.loadTrend(); } catch(_) {}
|
||||
_setHistoryBanner(false);
|
||||
} catch(e) {
|
||||
console.error('[history] failed to load open items:', e);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadHistorySession(refScanId) {
|
||||
// refScanId: null → all open (unreviewed) items across every scan,
|
||||
// positive int → a specific past session
|
||||
if (refScanId === null) return loadOpenItems();
|
||||
const resolvedRef = refScanId;
|
||||
|
||||
try {
|
||||
const r = await fetch('/api/db/flagged?ref=' + resolvedRef);
|
||||
|
||||
Reference in new issue
Block a user