Files
2026-09-16 23:20:08 +07:00

162 lines
7.2 KiB
PHP

<?php
$pageTitle = "AI Knowledge Base";
ob_start();
?>
<div class="mb-6">
<h1 class="text-2xl font-bold text-slate-800"><i class="fas fa-brain text-indigo-600 mr-2"></i> AI Knowledge Base</h1>
<p class="text-slate-500 mt-1">History of AI-generated remediation guidelines. These insights are cached to accelerate future incident responses.</p>
</div>
<div class="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden">
<div class="overflow-x-auto">
<table id="aiHistoryTable" class="w-full text-left border-collapse table-fixed">
<thead>
<tr class="bg-slate-50 text-slate-600 border-b border-slate-200 text-sm">
<th class="px-5 py-4 font-semibold w-64"><?= __('Rule Info') ?></th>
<th class="px-5 py-4 font-semibold"><?= __('AI Recommendation') ?></th>
<th class="px-5 py-4 font-semibold text-center w-40"><?= __('Cached Date') ?></th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100 text-sm">
<?php foreach ($histories as $h): ?>
<?php
// Extract rule groups from raw_data JSON
$groups = [];
if (isset($h['raw_data'])) {
$raw = json_decode($h['raw_data'], true);
if (isset($raw['rule']['groups'])) {
$groups = $raw['rule']['groups'];
}
}
$desc = htmlspecialchars($h['rule_desc'] ?? 'Unknown Rule Description');
?>
<tr class="hover:bg-slate-50/50 transition-colors group">
<td class="px-5 py-4">
<div class="font-mono text-xs text-indigo-600 font-bold mb-1">ID: <?= htmlspecialchars($h['rule_id']) ?></div>
<div class="text-slate-700 font-medium text-xs mb-2 leading-tight"><?= $desc ?></div>
<div class="flex flex-wrap gap-1">
<?php foreach($groups as $g): $g = trim($g); if(empty($g)) continue; ?>
<span class="bg-slate-100 text-slate-500 px-1.5 py-0.5 rounded text-[10px] uppercase font-semibold"><?= htmlspecialchars($g) ?></span>
<?php endforeach; ?>
</div>
</td>
<td class="px-5 py-4">
<div class="bg-indigo-50/50 border border-indigo-100 rounded-lg p-3 text-indigo-900 text-sm shadow-sm relative whitespace-normal break-words w-full" id="rec-container-<?= $h['rule_id'] ?>">
<div class="overflow-hidden line-clamp-3 max-h-[4.5rem]">
<?= $h['recommendation'] ?>
</div>
<div class="mt-2 pt-2 border-t border-indigo-100/50 flex justify-between items-center">
<button onclick="regenerateAI(<?= $h['rule_id'] ?>, '<?= addslashes($desc) ?>')" class="text-slate-400 hover:text-indigo-600 text-xs font-bold uppercase transition" id="regen-btn-<?= $h['rule_id'] ?>">
<i class="fas fa-sync-alt mr-1"></i> Regenerate
</button>
<button onclick="viewAIRecommendation(<?= $h['rule_id'] ?>)" class="text-indigo-600 hover:text-indigo-800 text-xs font-bold uppercase transition">
<i class="fas fa-expand-alt mr-1"></i> Read Full Analysis
</button>
</div>
</div>
<template id="ai-rec-<?= $h['rule_id'] ?>">
<?= $h['recommendation'] ?>
</template>
</td>
<td class="px-5 py-4 text-center text-slate-500 whitespace-nowrap">
<?= date('Y-m-d H:i', strtotime($h['created_at'])) ?>
</td>
</tr>
<?php endforeach; ?>
<?php if (empty($histories)): ?>
<tr>
<td colspan="3" class="px-5 py-8 text-center text-slate-400">
<i class="fas fa-inbox text-3xl mb-3 block"></i>
<?= __('No AI insights cached yet.') ?>
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<!-- DataTables setup -->
<script>
window.regenerateAI = function(ruleId, ruleDesc) {
let btn = document.getElementById('regen-btn-' + ruleId);
let container = document.getElementById('rec-container-' + ruleId);
let tmpl = document.getElementById('ai-rec-' + ruleId);
let originalIcon = btn.innerHTML;
btn.innerHTML = '<i class="fas fa-spinner fa-spin mr-1"></i> Thinking...';
btn.disabled = true;
$.ajax({
url: '<?= $baseUrl ?>/api/ai-remediation',
method: 'POST',
data: {
rule_id: ruleId,
rule_desc: ruleDesc,
level: 0,
force: 'true'
},
success: function(res) {
btn.innerHTML = originalIcon;
btn.disabled = false;
if (res.status === 'success') {
tmpl.innerHTML = res.html;
container.querySelector('.line-clamp-3').innerHTML = res.html;
Swal.fire({
icon: 'success',
title: 'Regenerated!',
text: 'The AI insight has been updated.',
timer: 1500,
showConfirmButton: false
});
} else {
Swal.fire('Error', res.message || 'Failed to regenerate.', 'error');
}
},
error: function() {
btn.innerHTML = originalIcon;
btn.disabled = false;
Swal.fire('Error', 'Connection failed.', 'error');
}
});
};
window.viewAIRecommendation = function(ruleId) {
let template = document.getElementById('ai-rec-' + ruleId);
if (!template) return;
let rawHtml = template.innerHTML;
Swal.fire({
title: '<div class="text-indigo-800 text-lg border-b border-indigo-200 pb-2"><i class="fas fa-robot mr-2"></i> AI Investigation & Remediation</div>',
html: `<div class="text-left text-sm text-indigo-900 bg-indigo-50 p-5 rounded-lg border border-indigo-100 leading-relaxed overflow-x-auto shadow-inner mt-2">${rawHtml}</div>`,
width: '800px',
showCloseButton: true,
showConfirmButton: false,
customClass: {
container: 'font-sans'
}
});
};
$(document).ready(function() {
if($('#aiHistoryTable tbody tr td').length > 1) {
$('#aiHistoryTable').DataTable({
responsive: true,
order: [[2, 'desc']],
pageLength: 10,
language: {
search: "",
searchPlaceholder: "Search insights..."
},
dom: '<"flex justify-between items-center mb-4 px-5 pt-4"lf>rt<"flex justify-between items-center mt-4 px-5 pb-4"ip>'
});
}
});
</script>
<?php
$content = ob_get_clean();
require 'layout.php';
?>