rajchavan's picture
can i run it and it will work
a586a16 verified
// Mock data for demonstration
const mockRuns = [
{
runId: "cfe1e00a-1823-4fd7-bca7-3f99eccf3620",
started_at: "2023-05-15T10:30:00Z",
ended_at: "2023-05-15T10:35:23Z",
status: "Completed",
completed_nodes: 9,
total_nodes: 9
},
{
runId: "d2e4f6a8-b1c3-4d5e-7f8g-9h0i1j2k3l4m",
started_at: "2023-05-15T11:15:00Z",
ended_at: null,
status: "Running",
completed_nodes: 4,
total_nodes: 9
},
{
runId: "a1b2c3d4-e5f6-7g8h-9i0j-k1l2m3n4o5p6",
started_at: "2023-05-14T09:45:00Z",
ended_at: "2023-05-14T09:47:12Z",
status: "Failed",
completed_nodes: 2,
total_nodes: 9
}
];
const mockRunDetails = {
"cfe1e00a-1823-4fd7-bca7-3f99eccf3620": {
runId: "cfe1e00a-1823-4fd7-bca7-3f99eccf3620",
status: "Completed",
nodes: [
{
node_name: "search/kb",
status: "success",
input_json: { query: "VPN not connecting" },
output_json: { results: ["KB001", "KB042", "KB123"] },
timestamp: "2023-05-15T10:30:15Z"
},
{
node_name: "validate/kb",
status: "success",
input_json: { kb_ids: ["KB001", "KB042", "KB123"] },
output_json: { valid_ids: ["KB001", "KB123"] },
timestamp: "2023-05-15T10:31:02Z"
},
// More nodes would be here...
]
}
};
document.addEventListener('DOMContentLoaded', () => {
// Initialize UI
renderRunsTable();
// Set up event listeners
document.getElementById('newRunBtn').addEventListener('click', startNewRun);
document.getElementById('closeModal').addEventListener('click', () => {
document.getElementById('runModal').classList.add('hidden');
});
});
function renderRunsTable() {
const tableBody = document.getElementById('runsTable');
tableBody.innerHTML = '';
mockRuns.forEach(run => {
const row = document.createElement('tr');
// Status badge
let statusBadge;
switch(run.status) {
case 'Completed':
statusBadge = `<span class="px-2 py-1 rounded-full text-xs font-semibold bg-green-100 text-green-800">Completed</span>`;
break;
case 'Running':
statusBadge = `<span class="px-2 py-1 rounded-full text-xs font-semibold bg-blue-100 text-blue-800 running-pulse">Running</span>`;
break;
case 'Failed':
statusBadge = `<span class="px-2 py-1 rounded-full text-xs font-semibold bg-red-100 text-red-800">Failed</span>`;
break;
default:
statusBadge = `<span class="px-2 py-1 rounded-full text-xs font-semibold bg-gray-100 text-gray-800">Pending</span>`;
}
// Progress bar
const progressPercent = run.completed_nodes / run.total_nodes * 100;
const progressBar = `
<div class="w-full bg-gray-200 rounded-full h-2.5">
<div class="bg-blue-600 h-2.5 rounded-full" style="width: ${progressPercent}%"></div>
</div>
<div class="text-xs text-gray-500 mt-1">${run.completed_nodes}/${run.total_nodes} nodes</div>
`;
// Shortened run ID
const shortRunId = run.runId.substring(0, 8) + '...';
row.innerHTML = `
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">${shortRunId}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${new Date(run.started_at).toLocaleString()}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${statusBadge}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${progressBar}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<button class="text-blue-600 hover:text-blue-800 view-run" data-runid="${run.runId}">
<i data-feather="eye" class="w-4 h-4"></i> View
</button>
</td>
`;
tableBody.appendChild(row);
});
// Add event listeners to view buttons
document.querySelectorAll('.view-run').forEach(button => {
button.addEventListener('click', (e) => {
const runId = e.target.closest('button').getAttribute('data-runid');
showRunDetails(runId);
});
});
feather.replace();
});
function showRunDetails(runId) {
const modal = document.getElementById('runModal');
const detailsContainer = document.getElementById('runDetails');
// In a real app, we would fetch this from the API
const runDetails = mockRunDetails[runId] || {
runId,
status: "Running",
nodes: []
};
// Render the flow diagram
detailsContainer.innerHTML = `
<div class="mb-6">
<h3 class="text-lg font-semibold mb-2">Run ID: ${runDetails.runId}</h3>
<div class="flex items-center">
<span class="mr-2">Status:</span>
${runDetails.status === 'Completed' ?
'<span class="px-2 py-1 rounded-full text-xs font-semibold bg-green-100 text-green-800">Completed</span>' :
runDetails.status === 'Running' ?
'<span class="px-2 py-1 rounded-full text-xs font-semibold bg-blue-100 text-blue-800 running-pulse">Running</span>' :
'<span class="px-2 py-1 rounded-full text-xs font-semibold bg-red-100 text-red-800">Failed</span>'}
</div>
</div>
<div class="mb-8">
<h3 class="text-lg font-semibold mb-4">Execution Flow</h3>
<div class="flex items-center justify-center overflow-x-auto py-4">
${renderFlowDiagram(runDetails.nodes)}
</div>
</div>
<div>
<h3 class="text-lg font-semibold mb-4">Node Details</h3>
<div class="space-y-4">
${runDetails.nodes.map(node => renderNodeDetails(node)).join('')}
</div>
</div>
`;
modal.classList.remove('hidden');
feather.replace();
}
function renderFlowDiagram(nodes) {
const allNodes = [
"search/kb", "validate/kb", "search/web", "steps/perform",
"script/generate", "email/send", "ticket/route", "ticket/create", "issue/auto-resolve"
];
let html = '';
for (let i = 0; i < allNodes.length; i++) {
const nodeName = allNodes[i];
const nodeData = nodes.find(n => n.node_name === nodeName);
// Determine node status
let nodeStatus;
if (nodeData) {
nodeStatus = nodeData.status === 'success' ? 'success' : 'failed';
} else {
nodeStatus = 'pending';
}
// Node element
html += `
<div class="node-container">
<div class="node ${nodeStatus === 'success' ? 'node-success' :
nodeStatus === 'failed' ? 'node-failed' :
nodeStatus === 'running' ? 'node-running running-pulse' : 'node-pending'}"
title="${nodeName}">
${i+1}
</div>
${i < allNodes.length - 1 ? `
<div class="flow-line ${nodeStatus === 'success' ? 'completed' :
nodeStatus === 'failed' ? 'failed' :
nodeStatus === 'running' ? 'active' : ''}"></div>
` : ''}
</div>
`;
}
return html;
}
function renderNodeDetails(node) {
return `
<div class="bg-gray-50 rounded-lg p-4">
<div class="flex justify-between items-center mb-2">
<h4 class="font-medium">${node.node_name}</h4>
<span class="px-2 py-1 rounded-full text-xs font-semibold ${
node.status === 'success' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
}">
${node.status}
</span>
</div>
<div class="text-sm text-gray-500 mb-2">${new Date(node.timestamp).toLocaleString()}</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-3">
<div>
<h5 class="text-sm font-medium mb-1">Input</h5>
<div class="json-viewer">${JSON.stringify(node.input_json, null, 2)}</div>
</div>
<div>
<h5 class="text-sm font-medium mb-1">Output</h5>
<div class="json-viewer">${JSON.stringify(node.output_json, null, 2)}</div>
</div>
</div>
</div>
`;
}
function startNewRun() {
// In a real app, this would call the API to start a new run
alert('Starting a new SmartTriage run...');
// Then we would navigate to the run details page or refresh the list
}