Add clear findings button to library maintenance modal
Per-job and per-status filtering — respects active toolbar filters so users can clear e.g. only track number findings or only dismissed items. Confirmation uses the app's styled modal instead of browser confirm().
This commit is contained in:
parent
658f5c60f7
commit
42a4285e09
5 changed files with 107 additions and 1 deletions
|
|
@ -1075,6 +1075,36 @@ class RepairWorker:
|
|||
if conn:
|
||||
conn.close()
|
||||
|
||||
def clear_findings(self, job_id: str = None, status: str = None) -> int:
|
||||
"""Delete findings from the database. Optionally filter by job_id and/or status. Returns count deleted."""
|
||||
conn = None
|
||||
try:
|
||||
conn = self.db._get_connection()
|
||||
cursor = conn.cursor()
|
||||
conditions = []
|
||||
params = []
|
||||
if job_id:
|
||||
conditions.append("job_id = ?")
|
||||
params.append(job_id)
|
||||
if status:
|
||||
conditions.append("status = ?")
|
||||
params.append(status)
|
||||
where = f" WHERE {' AND '.join(conditions)}" if conditions else ""
|
||||
cursor.execute(f"SELECT COUNT(*) FROM repair_findings{where}", params)
|
||||
count = cursor.fetchone()[0]
|
||||
cursor.execute(f"DELETE FROM repair_findings{where}", params)
|
||||
conn.commit()
|
||||
logger.info("Cleared %d findings%s%s", count,
|
||||
f" for job {job_id}" if job_id else "",
|
||||
f" with status {status}" if status else "")
|
||||
return count
|
||||
except Exception as e:
|
||||
logger.error("Error clearing findings: %s", e)
|
||||
return 0
|
||||
finally:
|
||||
if conn:
|
||||
conn.close()
|
||||
|
||||
def _get_findings_count(self, status: str = None) -> int:
|
||||
"""Get count of findings by status."""
|
||||
conn = None
|
||||
|
|
|
|||
|
|
@ -39031,6 +39031,23 @@ def repair_findings_bulk():
|
|||
logger.error(f"Error bulk updating findings: {e}")
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
@app.route('/api/repair/findings/clear', methods=['POST'])
|
||||
def repair_findings_clear():
|
||||
"""Clear (delete) findings, optionally filtered by job_id and/or status"""
|
||||
try:
|
||||
if repair_worker is None:
|
||||
return jsonify({'error': 'Repair worker not initialized'}), 400
|
||||
|
||||
data = request.get_json(silent=True) or {}
|
||||
job_id = data.get('job_id')
|
||||
status = data.get('status')
|
||||
|
||||
count = repair_worker.clear_findings(job_id=job_id, status=status)
|
||||
return jsonify({'success': True, 'deleted': count}), 200
|
||||
except Exception as e:
|
||||
logger.error(f"Error clearing findings: {e}")
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
@app.route('/api/repair/history', methods=['GET'])
|
||||
def repair_history():
|
||||
"""Get job run history"""
|
||||
|
|
|
|||
|
|
@ -5774,6 +5774,7 @@
|
|||
<button class="repair-bulk-btn fix" onclick="bulkFixFindings()">Fix Selected</button>
|
||||
<button class="repair-bulk-btn" onclick="bulkRepairAction('dismiss')">Dismiss Selected</button>
|
||||
</div>
|
||||
<button class="repair-clear-btn" onclick="clearRepairFindings()" title="Clear findings matching current filters">Clear Findings</button>
|
||||
</div>
|
||||
<div class="repair-findings-list" id="repair-findings-list">
|
||||
<div class="repair-loading">Loading findings...</div>
|
||||
|
|
|
|||
|
|
@ -52617,6 +52617,47 @@ async function bulkFixFindings() {
|
|||
updateRepairStatus();
|
||||
}
|
||||
|
||||
async function clearRepairFindings() {
|
||||
const jobFilter = document.getElementById('repair-findings-job-filter');
|
||||
const statusFilter = document.getElementById('repair-findings-status-filter');
|
||||
const jobId = jobFilter ? jobFilter.value : '';
|
||||
const status = statusFilter ? statusFilter.value : '';
|
||||
|
||||
const scopeLabel = jobId ? jobId.replace(/_/g, ' ') : 'all jobs';
|
||||
const statusLabel = status ? ` (${status})` : '';
|
||||
if (!await showConfirmDialog({
|
||||
title: 'Clear Findings',
|
||||
message: `Delete all findings for ${scopeLabel}${statusLabel}? This cannot be undone.`,
|
||||
confirmText: 'Clear',
|
||||
destructive: true
|
||||
})) return;
|
||||
|
||||
try {
|
||||
const body = {};
|
||||
if (jobId) body.job_id = jobId;
|
||||
if (status) body.status = status;
|
||||
|
||||
const response = await fetch('/api/repair/findings/clear', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
showToast(`Cleared ${result.deleted} findings`, 'success');
|
||||
} else {
|
||||
showToast(result.error || 'Failed to clear findings', 'error');
|
||||
}
|
||||
_repairSelectedFindings.clear();
|
||||
loadRepairFindingsDashboard();
|
||||
loadRepairFindings();
|
||||
updateRepairStatus();
|
||||
} catch (error) {
|
||||
console.error('Error clearing findings:', error);
|
||||
showToast('Error clearing findings', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function loadRepairHistory() {
|
||||
const container = document.getElementById('repair-history-list');
|
||||
if (!container) return;
|
||||
|
|
|
|||
|
|
@ -40803,7 +40803,7 @@ tr.tag-diff-same {
|
|||
.repair-findings-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 14px;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
|
|
@ -40872,6 +40872,23 @@ tr.tag-diff-same {
|
|||
background: rgba(var(--accent-rgb, 99, 102, 241), 0.2);
|
||||
}
|
||||
|
||||
.repair-clear-btn {
|
||||
margin-left: auto;
|
||||
background: rgba(239, 68, 68, 0.08);
|
||||
border: 1px solid rgba(239, 68, 68, 0.2);
|
||||
border-radius: 6px;
|
||||
padding: 5px 12px;
|
||||
color: #f87171;
|
||||
font-size: 11px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.repair-clear-btn:hover {
|
||||
background: rgba(239, 68, 68, 0.18);
|
||||
border-color: rgba(239, 68, 68, 0.4);
|
||||
}
|
||||
|
||||
.repair-findings-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
|||
Loading…
Reference in a new issue