test_ui.html•41.2 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fabric MCP Agent - Test UI</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
color: white;
padding: 30px;
text-align: center;
}
.header h1 {
font-size: 2.5em;
margin-bottom: 10px;
}
.header p {
font-size: 1.1em;
opacity: 0.9;
}
.content {
padding: 30px;
}
.test-section {
margin-bottom: 40px;
background: #f8f9fa;
border-radius: 10px;
padding: 25px;
border-left: 5px solid #4facfe;
}
.test-section h2 {
color: #333;
margin-bottom: 20px;
font-size: 1.5em;
}
.input-group {
margin-bottom: 20px;
}
.input-group label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #555;
}
.input-group input, .input-group textarea, .input-group select {
width: 100%;
padding: 12px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 14px;
transition: border-color 0.3s;
}
.input-group input:focus, .input-group textarea:focus, .input-group select:focus {
outline: none;
border-color: #4facfe;
}
.btn {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
color: white;
border: none;
padding: 12px 25px;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: transform 0.2s;
}
.btn:hover {
transform: translateY(-2px);
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.response {
margin-top: 20px;
padding: 20px;
background: white;
border-radius: 8px;
border: 1px solid #e0e0e0;
max-height: 400px;
overflow-y: auto;
}
.response pre {
white-space: pre-wrap;
font-family: 'Courier New', monospace;
font-size: 12px;
line-height: 1.4;
}
.loading {
display: none;
color: #666;
font-style: italic;
}
.error {
color: #e74c3c;
background: #fdf2f2;
border: 1px solid #f5c6cb;
}
.success {
color: #27ae60;
background: #f2fff2;
border: 1px solid #c3e6cb;
}
.quick-tests {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin-top: 20px;
}
.quick-test-btn {
background: #e9ecef;
color: #333;
border: 2px solid #e9ecef;
padding: 10px 15px;
border-radius: 8px;
cursor: pointer;
font-size: 13px;
transition: all 0.3s;
}
.quick-test-btn:hover {
background: #4facfe;
color: white;
border-color: #4facfe;
}
/* Tab Navigation Styles */
.tab-navigation {
background: #f8f9fa;
border-bottom: 2px solid #e9ecef;
padding: 0 30px;
display: flex;
gap: 0;
}
.tab-button {
background: transparent;
border: none;
padding: 15px 25px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
color: #666;
border-bottom: 3px solid transparent;
transition: all 0.3s ease;
}
.tab-button:hover {
color: #4facfe;
background: rgba(79, 172, 254, 0.1);
}
.tab-button.active {
color: #4facfe;
border-bottom-color: #4facfe;
background: white;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
/* Prompt Editor Styles */
.prompt-editor {
display: grid;
grid-template-columns: 250px 1fr;
gap: 20px;
height: 600px;
}
.prompt-list {
background: #f8f9fa;
border-radius: 8px;
padding: 15px;
overflow-y: auto;
}
.prompt-item {
padding: 10px 15px;
margin-bottom: 5px;
background: white;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s;
border: 1px solid #e0e0e0;
}
.prompt-item:hover {
background: #4facfe;
color: white;
}
.prompt-item.selected {
background: #4facfe;
color: white;
}
.prompt-editor-panel {
display: flex;
flex-direction: column;
gap: 15px;
}
.prompt-editor-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
}
.prompt-editor-textarea {
flex: 1;
min-height: 400px;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 13px;
line-height: 1.5;
resize: none;
}
.prompt-actions {
display: flex;
gap: 10px;
align-items: center;
}
.btn-secondary {
background: #6c757d;
color: white;
}
.btn-success {
background: #28a745;
color: white;
}
.btn-small {
padding: 8px 16px;
font-size: 12px;
}
.endpoint-info {
background: #e3f2fd;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
border-left: 4px solid #2196f3;
}
.endpoint-info code {
background: #fff;
padding: 2px 6px;
border-radius: 4px;
font-family: 'Courier New', monospace;
}
.sql-results-table {
margin-top: 15px;
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.sql-results-table table {
width: 100%;
border-collapse: collapse;
}
.sql-results-table th {
background: #4facfe;
color: white;
padding: 12px 8px;
text-align: left;
font-weight: 600;
font-size: 13px;
}
.sql-results-table td {
padding: 10px 8px;
border-bottom: 1px solid #e0e0e0;
font-size: 13px;
}
.sql-results-table tr:nth-child(even) {
background: #f8f9fa;
}
.sql-results-table tr:hover {
background: #e3f2fd;
}
.no-results {
text-align: center;
color: #666;
font-style: italic;
padding: 20px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🔷 Fabric MCP Agent</h1>
<p>Test UI for Microsoft Fabric Data Warehouse MCP Server with Agentic Intelligence</p>
</div>
<!-- Tab Navigation -->
<div class="tab-navigation">
<button class="tab-button active" onclick="switchTab(event, 'agentic-tab')">🧠 Agentic Intelligence</button>
<button class="tab-button" onclick="switchTab(event, 'tools-tab')">🔧 MCP Tools</button>
<button class="tab-button" onclick="switchTab(event, 'prompts-tab')">📝 Prompt Management</button>
</div>
<div class="content">
<!-- Agentic Intelligence Tab -->
<div id="agentic-tab" class="tab-content active">
<div class="test-section">
<h2>🧠 Agentic Intelligence (Recommended)</h2>
<div class="endpoint-info">
<strong>Endpoint:</strong> <code>POST /mcp</code><br>
Uses intent classification, prompt modules, and tool chaining for enriched responses.
</div>
<div class="input-group">
<label for="agenticQuestion">Ask a business question:</label>
<textarea id="agenticQuestion" rows="3" placeholder="tell me the components in MRH-011C"></textarea>
</div>
<button class="btn" onclick="testAgentic()">🚀 Ask with AI Reasoning</button>
<div class="loading" id="agenticLoading">Processing with agentic intelligence...</div>
<div class="quick-tests">
<button class="quick-test-btn" onclick="setAgenticQuestion('Hogy quoted us a surgical kit with BD Luer-Lock Syringe 2.5mL. What\'s our equivalent?')">Complex AI Query (Fallback)</button>
<button class="quick-test-btn" onclick="setAgenticQuestion('what\'s our product for hogy BR-56U10')">Direct Tool Query</button>
<button class="quick-test-btn" onclick="setAgenticQuestion('Show me all products like MRH-011C')">Similar Products</button>
<button class="quick-test-btn" onclick="setAgenticQuestion('What product types are available?')">Product Types</button>
</div>
<div class="response" id="agenticResponse"></div>
<!-- SQL Results Table -->
<div id="sqlResultsTable" style="display: none; margin-top: 20px;">
<h3>📊 Query Results</h3>
<div id="resultsContainer"></div>
</div>
</div>
</div>
<!-- MCP Tools Tab -->
<div id="tools-tab" class="tab-content">
<div class="test-section">
<h2>🔧 Direct MCP Tool Testing</h2>
<div class="endpoint-info">
<strong>Endpoint:</strong> <code>POST /call_tool</code><br>
Direct access to individual MCP tools for debugging and testing. Advanced users can pass custom parameters.
</div>
<div class="input-group">
<label for="toolSelect2">Select Tool:</label>
<select id="toolSelect2" onchange="updateToolForm()">
<option value="run_sql_query">run_sql_query</option>
<option value="get_metadata">get_metadata</option>
<option value="summarize_results">summarize_results</option>
<option value="generate_visualization">generate_visualization</option>
</select>
</div>
<div id="toolArgsForm">
<!-- Dynamic form based on selected tool -->
</div>
<button class="btn" onclick="testTool()" id="toolButton">Execute Tool</button>
<div class="loading" id="toolLoading">Executing tool...</div>
<div class="response" id="toolResponse"></div>
</div>
<!-- Schema Discovery -->
<div class="test-section">
<h2>📊 Schema Discovery</h2>
<div class="endpoint-info">
<strong>Endpoint:</strong> <code>GET /list_tools</code><br>
Discover all available MCP tools and their schemas.
</div>
<button class="btn" onclick="listTools()">📋 List Available Tools</button>
<div class="loading" id="schemaLoading">Loading tools...</div>
<div class="response" id="schemaResponse"></div>
</div>
</div>
<!-- Prompt Management Tab -->
<div id="prompts-tab" class="tab-content">
<div class="test-section">
<h2>📝 Prompt Module Management</h2>
<div class="endpoint-info">
<strong>Business User Control:</strong> Edit prompt modules to customize AI behavior for your domain expertise.<br>
Changes are automatically backed up and take effect immediately.
</div>
<div class="prompt-editor">
<div class="prompt-list">
<h3 style="margin-bottom: 15px; color: #333;">Available Prompts</h3>
<div id="promptList">
<div class="loading">Loading prompts...</div>
</div>
</div>
<div class="prompt-editor-panel">
<div class="prompt-editor-header">
<div>
<h3 id="currentPromptName">Select a prompt to edit</h3>
<small id="currentPromptInfo" style="color: #666;"></small>
</div>
<div class="prompt-actions">
<button class="btn btn-secondary btn-small" onclick="viewBackups()">📋 View Backups</button>
<button class="btn btn-success btn-small" onclick="savePrompt()" id="savePromptBtn" disabled>💾 Save Changes</button>
</div>
</div>
<textarea
id="promptEditor"
class="prompt-editor-textarea input-group"
placeholder="Select a prompt module from the left to begin editing..."
disabled
></textarea>
<div id="promptSaveStatus" class="response" style="display: none;"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
const API_BASE = 'http://localhost:8000';
const SUBSCRIPTION_KEY = '2c405c6d95ea493985aeda1985e91bf7'; // Replace with your actual key
// Agentic Intelligence Test
async function testAgentic() {
const question = document.getElementById('agenticQuestion').value;
if (!question.trim()) {
alert('Please enter a question');
return;
}
showLoading('agenticLoading', true);
clearResponse('agenticResponse');
try {
const response = await fetch(`${API_BASE}/mcp`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Ocp-Apim-Subscription-Key': SUBSCRIPTION_KEY
},
body: JSON.stringify({ question })
});
const result = await response.json();
showResponse('agenticResponse', result, response.ok);
// Parse and display results based on execution strategy
if (result.tool_chain_results && result.tool_chain_results.stage3_evaluation) {
displayAgenticResults(result);
} else if (result.tool_chain_results && result.tool_chain_results.run_sql_query) {
displaySQLResults(result.tool_chain_results.run_sql_query.results);
}
} catch (error) {
showResponse('agenticResponse', { error: error.message }, false);
} finally {
showLoading('agenticLoading', false);
}
}
function setAgenticQuestion(question) {
document.getElementById('agenticQuestion').value = question;
}
// MCP Tool Test
async function testTool() {
const tool = document.getElementById('toolSelect2').value;
const args = collectToolArgs();
showLoading('toolLoading', true);
clearResponse('toolResponse');
try {
const response = await fetch(`${API_BASE}/call_tool`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Ocp-Apim-Subscription-Key': SUBSCRIPTION_KEY
},
body: JSON.stringify({ tool, args })
});
const result = await response.json();
showResponse('toolResponse', result, response.ok);
} catch (error) {
showResponse('toolResponse', { error: error.message }, false);
} finally {
showLoading('toolLoading', false);
}
}
function updateToolForm() {
const tool = document.getElementById('toolSelect2').value;
const formContainer = document.getElementById('toolArgsForm');
let formHTML = '';
switch (tool) {
case 'run_sql_query':
formHTML = `
<div class="input-group">
<label>Question (natural language):</label>
<textarea id="sqlQuestion" rows="3" placeholder="What are the components in MRH-011C?"></textarea>
<small style="color: #666;">Enter a natural language question to convert to SQL</small>
</div>
<div style="text-align: center; margin: 10px 0; color: #999; font-weight: bold;">— OR —</div>
<div class="input-group">
<label>Direct SQL:</label>
<textarea id="directSQL" rows="3" placeholder="SELECT pt.part_number, pt.description FROM JPNPROdb_pt_mstr pt JOIN JPNPROdb_ps_mstr ps ON pt.product_id = ps.product_id WHERE ps.product_code = 'MRH-011C'"></textarea>
<small style="color: #666;">Enter raw SQL to execute directly</small>
</div>
`;
break;
case 'get_metadata':
formHTML = `
<div class="input-group">
<label>Table Name (optional):</label>
<input type="text" id="tableName" placeholder="JPNPROdb_ps_mstr">
</div>
`;
break;
case 'summarize_results':
formHTML = `
<div class="input-group">
<label>Data (JSON array):</label>
<textarea id="dataToSummarize" rows="3" placeholder='[{"product": "A", "count": 10}]'></textarea>
</div>
<div class="input-group">
<label>Context:</label>
<input type="text" id="summaryContext" placeholder="product_planning">
</div>
`;
break;
case 'generate_visualization':
formHTML = `
<div class="input-group">
<label>Data (JSON array):</label>
<textarea id="vizData" rows="3" placeholder='[{"category": "A", "value": 10}]'></textarea>
</div>
<div class="input-group">
<label>Chart Type:</label>
<select id="chartType">
<option value="table">Table</option>
<option value="bar">Bar Chart</option>
<option value="line">Line Chart</option>
<option value="pie">Pie Chart</option>
</select>
</div>
<div class="input-group">
<label>Title:</label>
<input type="text" id="chartTitle" placeholder="Product Analysis">
</div>
`;
break;
}
formContainer.innerHTML = formHTML;
}
function collectToolArgs() {
const tool = document.getElementById('toolSelect2').value;
const args = {};
switch (tool) {
case 'run_sql_query':
const question = document.getElementById('sqlQuestion').value;
const sql = document.getElementById('directSQL').value;
if (question.trim()) args.question = question;
if (sql.trim()) args.sql = sql;
break;
case 'get_metadata':
const tableName = document.getElementById('tableName').value;
if (tableName.trim()) args.table_name = tableName;
break;
case 'summarize_results':
const data = document.getElementById('dataToSummarize').value;
const context = document.getElementById('summaryContext').value;
try {
args.data = JSON.parse(data || '[]');
} catch (e) {
args.data = [];
}
if (context.trim()) args.context = context;
break;
case 'generate_visualization':
const vizData = document.getElementById('vizData').value;
const chartType = document.getElementById('chartType').value;
const title = document.getElementById('chartTitle').value;
try {
args.data = JSON.parse(vizData || '[]');
} catch (e) {
args.data = [];
}
if (chartType) args.chart_type = chartType;
if (title.trim()) args.title = title;
break;
}
return args;
}
// Schema Discovery
async function listTools() {
showLoading('schemaLoading', true);
clearResponse('schemaResponse');
try {
const response = await fetch(`${API_BASE}/list_tools`, {
headers: {
'Ocp-Apim-Subscription-Key': SUBSCRIPTION_KEY
}
});
const result = await response.json();
showResponse('schemaResponse', result, response.ok);
} catch (error) {
showResponse('schemaResponse', { error: error.message }, false);
} finally {
showLoading('schemaLoading', false);
}
}
// Utility Functions
function showLoading(elementId, show) {
document.getElementById(elementId).style.display = show ? 'block' : 'none';
}
function clearResponse(elementId) {
const element = document.getElementById(elementId);
element.innerHTML = '';
element.className = 'response';
}
function showResponse(elementId, data, success) {
const element = document.getElementById(elementId);
element.innerHTML = `<pre>${JSON.stringify(data, null, 2)}</pre>`;
element.className = `response ${success ? 'success' : 'error'}`;
}
function displayAgenticResults(result) {
const tableContainer = document.getElementById('sqlResultsTable');
const resultsContainer = document.getElementById('resultsContainer');
// Clear previous results
resultsContainer.innerHTML = '';
let html = '';
// Display Stage 3 Business Analysis if available
if (result.tool_chain_results && result.tool_chain_results.stage3_evaluation) {
const stage3 = result.tool_chain_results.stage3_evaluation;
html += `
<div class="stage3-analysis" style="margin-bottom: 30px; padding: 20px; background: #f0f8ff; border-radius: 10px; border-left: 5px solid #4facfe;">
<h3 style="color: #333; margin-bottom: 15px;">🎯 Business Analysis</h3>
<div style="margin-bottom: 20px;">
<h4 style="color: #4facfe; margin-bottom: 10px;">Answer:</h4>
<p style="font-size: 16px; line-height: 1.6; color: #333;">${stage3.business_answer}</p>
</div>
<div style="margin-bottom: 20px;">
<h4 style="color: #4facfe; margin-bottom: 10px;">Key Findings:</h4>
<ul style="padding-left: 20px;">
${stage3.key_findings.map(finding => `<li style="margin-bottom: 8px; color: #555;">${finding}</li>`).join('')}
</ul>
</div>
<div style="margin-bottom: 20px;">
<h4 style="color: #4facfe; margin-bottom: 10px;">Recommended Action:</h4>
<p style="font-style: italic; color: #666; background: #fff; padding: 10px; border-radius: 5px;">${stage3.recommended_action}</p>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-top: 15px;">
<div>
<strong style="color: #4facfe;">Confidence:</strong>
<span style="padding: 3px 8px; background: ${stage3.supporting_data.confidence === 'high' ? '#28a745' : stage3.supporting_data.confidence === 'medium' ? '#ffc107' : '#dc3545'}; color: white; border-radius: 3px; font-size: 12px;">${stage3.supporting_data.confidence}</span>
</div>
<div>
<strong style="color: #4facfe;">Data Quality:</strong>
<span style="color: #666;">${stage3.data_quality}</span>
</div>
</div>
</div>
`;
}
// Display detailed SQL results if available
if (result.tool_chain_results && result.tool_chain_results.stage2_query && result.tool_chain_results.stage2_query.results) {
const sqlResults = result.tool_chain_results.stage2_query.results;
html += '<h3 style="color: #333; margin-bottom: 15px;">📊 Detailed Data</h3>';
html += generateSQLTable(sqlResults);
} else if (result.tool_chain_results && result.tool_chain_results.run_sql_query && result.tool_chain_results.run_sql_query.results) {
const sqlResults = result.tool_chain_results.run_sql_query.results;
html += '<h3 style="color: #333; margin-bottom: 15px;">📊 Query Results</h3>';
html += generateSQLTable(sqlResults);
}
// Display summary if available
if (result.tool_chain_results && result.tool_chain_results.summarize_results) {
const summary = result.tool_chain_results.summarize_results;
html += `
<div style="margin-top: 20px; padding: 15px; background: #f8f9fa; border-radius: 8px;">
<h4 style="color: #333; margin-bottom: 10px;">📋 Data Summary</h4>
<p style="color: #666;">${summary.summary}</p>
<small style="color: #999;">Records found: ${summary.row_count || 'N/A'}</small>
</div>
`;
}
if (html) {
resultsContainer.innerHTML = html;
tableContainer.style.display = 'block';
} else {
resultsContainer.innerHTML = '<div class="no-results">No results to display</div>';
tableContainer.style.display = 'block';
}
}
function generateSQLTable(results) {
if (!results || results.length === 0) {
return '<div class="no-results">No data found</div>';
}
const keys = Object.keys(results[0]);
let tableHTML = `
<div class="sql-results-table">
<table>
<thead>
<tr>
${keys.map(key => `<th>${key}</th>`).join('')}
</tr>
</thead>
<tbody>
${results.map(row => `
<tr>
${keys.map(key => `<td>${row[key] || ''}</td>`).join('')}
</tr>
`).join('')}
</tbody>
</table>
</div>
`;
return tableHTML;
}
function displaySQLResults(results) {
const tableContainer = document.getElementById('sqlResultsTable');
const resultsContainer = document.getElementById('resultsContainer');
if (!results || results.length === 0) {
resultsContainer.innerHTML = '<div class="no-results">No results found</div>';
tableContainer.style.display = 'block';
return;
}
// Create table
const table = document.createElement('table');
const thead = document.createElement('thead');
const tbody = document.createElement('tbody');
// Create header row
const headerRow = document.createElement('tr');
const keys = Object.keys(results[0]);
keys.forEach(key => {
const th = document.createElement('th');
th.textContent = key;
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
// Create data rows
results.forEach(row => {
const tr = document.createElement('tr');
keys.forEach(key => {
const td = document.createElement('td');
td.textContent = row[key] || '';
tr.appendChild(td);
});
tbody.appendChild(tr);
});
table.appendChild(thead);
table.appendChild(tbody);
resultsContainer.innerHTML = '';
const tableWrapper = document.createElement('div');
tableWrapper.className = 'sql-results-table';
tableWrapper.appendChild(table);
resultsContainer.appendChild(tableWrapper);
tableContainer.style.display = 'block';
}
// Tab Management
function switchTab(event, tabName) {
// Hide all tab contents
const tabContents = document.querySelectorAll('.tab-content');
tabContents.forEach(tab => {
tab.classList.remove('active');
});
// Remove active class from all tab buttons
const tabButtons = document.querySelectorAll('.tab-button');
tabButtons.forEach(button => {
button.classList.remove('active');
});
// Show selected tab and activate button
document.getElementById(tabName).classList.add('active');
event.target.classList.add('active');
// Load data for prompt tab
if (tabName === 'prompts-tab') {
loadPromptList();
}
}
// Prompt Management Functions
let currentPrompt = null;
let originalContent = '';
async function loadPromptList() {
try {
const response = await fetch(`${API_BASE}/prompts`, {
headers: {
'Ocp-Apim-Subscription-Key': SUBSCRIPTION_KEY
}
});
const data = await response.json();
const promptList = document.getElementById('promptList');
promptList.innerHTML = '';
data.prompts.forEach(prompt => {
const promptItem = document.createElement('div');
promptItem.className = 'prompt-item';
promptItem.textContent = prompt;
promptItem.onclick = () => loadPrompt(prompt);
promptList.appendChild(promptItem);
});
} catch (error) {
document.getElementById('promptList').innerHTML = '<div class="error">Error loading prompts: ' + error.message + '</div>';
}
}
async function loadPrompt(promptName) {
try {
// Update UI selection
document.querySelectorAll('.prompt-item').forEach(item => {
item.classList.remove('selected');
});
event.target.classList.add('selected');
const response = await fetch(`${API_BASE}/prompts/${promptName}`, {
headers: {
'Ocp-Apim-Subscription-Key': SUBSCRIPTION_KEY
}
});
const data = await response.json();
currentPrompt = promptName;
originalContent = data.content;
document.getElementById('currentPromptName').textContent = `Editing: ${promptName}`;
document.getElementById('currentPromptInfo').textContent = `Last modified: ${new Date(data.last_modified * 1000).toLocaleString()}`;
document.getElementById('promptEditor').value = data.content;
document.getElementById('promptEditor').disabled = false;
document.getElementById('savePromptBtn').disabled = false;
// Add change detection
document.getElementById('promptEditor').oninput = () => {
const hasChanges = document.getElementById('promptEditor').value !== originalContent;
document.getElementById('savePromptBtn').textContent = hasChanges ? '💾 Save Changes*' : '💾 Save Changes';
};
} catch (error) {
showPromptStatus('Error loading prompt: ' + error.message, 'error');
}
}
async function savePrompt() {
if (!currentPrompt) return;
const content = document.getElementById('promptEditor').value;
document.getElementById('savePromptBtn').disabled = true;
document.getElementById('savePromptBtn').textContent = '⏳ Saving...';
try {
const response = await fetch(`${API_BASE}/prompts/${currentPrompt}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Ocp-Apim-Subscription-Key': SUBSCRIPTION_KEY
},
body: JSON.stringify({
module_name: currentPrompt,
content: content
})
});
const result = await response.json();
if (response.ok) {
originalContent = content;
showPromptStatus(result.message, 'success');
if (result.backup_created) {
showPromptStatus(result.message + ` (Backup: ${result.backup_created})`, 'success');
}
} else {
showPromptStatus('Error: ' + result.detail, 'error');
}
} catch (error) {
showPromptStatus('Network error: ' + error.message, 'error');
} finally {
document.getElementById('savePromptBtn').disabled = false;
document.getElementById('savePromptBtn').textContent = '💾 Save Changes';
}
}
async function viewBackups() {
if (!currentPrompt) return;
try {
const response = await fetch(`${API_BASE}/prompts/${currentPrompt}/backups`, {
headers: {
'Ocp-Apim-Subscription-Key': SUBSCRIPTION_KEY
}
});
const data = await response.json();
if (data.backups.length === 0) {
showPromptStatus('No backups found for this prompt.', 'success');
} else {
let backupList = `Found ${data.backups.length} backups:\n\n`;
data.backups.forEach(backup => {
backupList += `📄 ${backup.filename} (${new Date(backup.timestamp * 1000).toLocaleString()}) - ${backup.size} bytes\n`;
});
showPromptStatus(backupList, 'success');
}
} catch (error) {
showPromptStatus('Error loading backups: ' + error.message, 'error');
}
}
function showPromptStatus(message, type = 'success') {
const statusDiv = document.getElementById('promptSaveStatus');
statusDiv.className = `response ${type}`;
statusDiv.innerHTML = `<pre>${message}</pre>`;
statusDiv.style.display = 'block';
// Auto-hide after 5 seconds
setTimeout(() => {
statusDiv.style.display = 'none';
}, 5000);
}
// Initialize
document.addEventListener('DOMContentLoaded', function() {
updateToolForm();
});
</script>
</body>
</html>