183 lines
5.5 KiB
JavaScript
183 lines
5.5 KiB
JavaScript
const form = document.getElementById("query-form");
|
|
const resultsPanel = document.getElementById("results");
|
|
const errorPanel = document.getElementById("error");
|
|
const warningEl = document.getElementById("warning");
|
|
const summaryEl = document.getElementById("summary");
|
|
const generatedSqlEl = document.getElementById("generated-sql");
|
|
const sanitizedBlock = document.getElementById("sanitized-block");
|
|
const sanitizedSqlEl = document.getElementById("sanitized-sql");
|
|
const rowCountEl = document.getElementById("row-count");
|
|
const tableEl = document.getElementById("result-table");
|
|
const tableHeadEl = document.getElementById("result-thead");
|
|
const tableBodyEl = document.getElementById("result-tbody");
|
|
const errorMessageEl = document.getElementById("error-message");
|
|
const feedbackPanel = document.getElementById("feedback-panel");
|
|
const feedbackCorrectBtn = document.getElementById("feedback-correct");
|
|
const feedbackIncorrectBtn = document.getElementById("feedback-incorrect");
|
|
const feedbackStatus = document.getElementById("feedback-status");
|
|
|
|
let latestResult = null;
|
|
|
|
function resetPanels() {
|
|
resultsPanel.hidden = true;
|
|
errorPanel.hidden = true;
|
|
warningEl.hidden = true;
|
|
warningEl.textContent = "";
|
|
summaryEl.textContent = "";
|
|
generatedSqlEl.textContent = "";
|
|
sanitizedBlock.hidden = true;
|
|
sanitizedSqlEl.textContent = "";
|
|
rowCountEl.textContent = "";
|
|
tableEl.hidden = true;
|
|
tableHeadEl.innerHTML = "";
|
|
tableBodyEl.innerHTML = "";
|
|
errorMessageEl.textContent = "";
|
|
feedbackPanel.hidden = true;
|
|
feedbackStatus.hidden = true;
|
|
feedbackStatus.textContent = "";
|
|
latestResult = null;
|
|
}
|
|
|
|
function renderTable(columns, rows) {
|
|
if (!columns || !rows || rows.length === 0) {
|
|
tableEl.hidden = true;
|
|
return;
|
|
}
|
|
|
|
const headerRow = document.createElement("tr");
|
|
columns.forEach((col) => {
|
|
const th = document.createElement("th");
|
|
th.textContent = col;
|
|
headerRow.appendChild(th);
|
|
});
|
|
tableHeadEl.appendChild(headerRow);
|
|
|
|
rows.forEach((row) => {
|
|
const tr = document.createElement("tr");
|
|
columns.forEach((col) => {
|
|
const td = document.createElement("td");
|
|
const value = row[col];
|
|
td.textContent = value === null || value === undefined ? "" : String(value);
|
|
tr.appendChild(td);
|
|
});
|
|
tableBodyEl.appendChild(tr);
|
|
});
|
|
|
|
tableEl.hidden = false;
|
|
}
|
|
|
|
form.addEventListener("submit", async (event) => {
|
|
event.preventDefault();
|
|
resetPanels();
|
|
|
|
const question = document.getElementById("question").value.trim();
|
|
const tablesValue = document.getElementById("tables").value.trim();
|
|
const execute = document.getElementById("execute").checked;
|
|
const maxRows = parseInt(document.getElementById("max-rows").value, 10) || 500;
|
|
|
|
if (!question) {
|
|
errorPanel.hidden = false;
|
|
errorMessageEl.textContent = "Please enter a question.";
|
|
return;
|
|
}
|
|
|
|
const payload = {
|
|
question,
|
|
tables: tablesValue ? tablesValue.split(",").map((s) => s.trim()).filter(Boolean) : undefined,
|
|
execute,
|
|
max_rows: maxRows,
|
|
feedback: null,
|
|
};
|
|
|
|
try {
|
|
const response = await fetch("/api/query", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(payload),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json().catch(() => ({}));
|
|
throw new Error(errorData.detail || `Request failed with status ${response.status}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
|
|
summaryEl.textContent = data.summary || "(No summary provided)";
|
|
generatedSqlEl.textContent = data.sql || "";
|
|
|
|
if (data.llm_warning) {
|
|
warningEl.textContent = data.llm_warning;
|
|
warningEl.hidden = false;
|
|
}
|
|
|
|
if (data.sanitized_sql) {
|
|
sanitizedSqlEl.textContent = data.sanitized_sql;
|
|
sanitizedBlock.hidden = false;
|
|
}
|
|
|
|
if (typeof data.row_count === "number") {
|
|
rowCountEl.textContent = `Rows returned: ${data.row_count}`;
|
|
}
|
|
|
|
renderTable(data.columns, data.rows);
|
|
|
|
latestResult = {
|
|
question,
|
|
sql: data.sql || "",
|
|
summary: data.summary || "",
|
|
sanitized_sql: data.sanitized_sql || null,
|
|
};
|
|
feedbackPanel.hidden = false;
|
|
feedbackStatus.hidden = true;
|
|
feedbackStatus.textContent = "";
|
|
|
|
resultsPanel.hidden = false;
|
|
} catch (error) {
|
|
errorPanel.hidden = false;
|
|
errorMessageEl.textContent = error.message || String(error);
|
|
}
|
|
});
|
|
|
|
async function sendFeedback(tag) {
|
|
if (!latestResult) {
|
|
return;
|
|
}
|
|
|
|
const payload = {
|
|
question: latestResult.question,
|
|
sql: latestResult.sql,
|
|
summary: latestResult.summary,
|
|
sanitized_sql: latestResult.sanitized_sql,
|
|
feedback: tag,
|
|
};
|
|
|
|
feedbackStatus.hidden = false;
|
|
feedbackStatus.textContent = "Sending feedback...";
|
|
feedbackCorrectBtn.disabled = true;
|
|
feedbackIncorrectBtn.disabled = true;
|
|
|
|
try {
|
|
const response = await fetch("/api/feedback", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(payload),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json().catch(() => ({}));
|
|
throw new Error(errorData.detail || `Feedback request failed with status ${response.status}`);
|
|
}
|
|
|
|
feedbackStatus.textContent = tag === "correct" ? "Marked as correct." : "Marked as needs work.";
|
|
} catch (error) {
|
|
feedbackStatus.textContent = error.message || String(error);
|
|
} finally {
|
|
feedbackCorrectBtn.disabled = false;
|
|
feedbackIncorrectBtn.disabled = false;
|
|
}
|
|
}
|
|
|
|
feedbackCorrectBtn.addEventListener("click", () => sendFeedback("correct"));
|
|
feedbackIncorrectBtn.addEventListener("click", () => sendFeedback("incorrect"));
|