Tweaked design of create query page

This commit is contained in:
Simon Willison 2026-05-26 14:02:37 -07:00
commit f7e9dbc27e
2 changed files with 49 additions and 12 deletions

View file

@ -94,12 +94,18 @@ form.sql .query-create-sql textarea#sql-editor {
.query-create-options input[type=checkbox] {
margin: 0;
}
.query-create-option-note {
.query-create-option-note,
.query-create-analysis-note {
color: #4f5b6d;
flex-basis: 100%;
font-size: 0.82rem;
}
.query-create-option-note {
margin: -0.45rem 0 0;
}
.query-create-analysis-note {
margin: 0;
}
.query-create-action {
margin: 0.35rem 0 1rem;
}
@ -160,9 +166,9 @@ form.sql .query-create-sql textarea#sql-editor {
<p class="query-create-sql sql-editor"><textarea id="sql-editor" name="sql"{% if sql %} style="height: {{ sql.split("\n")|length + 2 }}em"{% endif %}>{{ sql }}</textarea></p>
<p class="query-create-options">
<span class="query-create-analysis-note" data-query-create-analysis-note aria-live="polite">{% if analysis_error %}This query cannot be saved until the SQL is valid.{% elif not has_sql %}Enter SQL to analyze this query.{% elif analysis_is_write %}This query updates data in the database.{% else %}This is a read-only query.{% endif %}</span>
<input type="hidden" name="is_private" value="0">
<label><input type="checkbox" name="is_private" value="1"{% if is_private %} checked{% endif %}> Private</label>
<label><input type="checkbox" data-query-create-writable{% if analysis_is_write %} checked{% endif %} disabled> Writable</label>
<span class="query-create-option-note">Queries marked private can only be seen by you, their creator.</span>
</p>
{% if sql and analysis_is_write %}
@ -249,10 +255,25 @@ window.addEventListener("DOMContentLoaded", () => {
const submitButton = form
? form.querySelector("[data-query-create-submit]")
: null;
const writableCheckbox = form
? form.querySelector("[data-query-create-writable]")
const analysisNote = form
? form.querySelector("[data-query-create-analysis-note]")
: null;
function updateAnalysisNote(data) {
if (!analysisNote) {
return;
}
if (data.analysis_error) {
analysisNote.textContent = "This query cannot be saved until the SQL is valid.";
} else if (data.has_sql === false) {
analysisNote.textContent = "Enter SQL to analyze this query.";
} else if (data.analysis_is_write) {
analysisNote.textContent = "This query updates data in the database.";
} else {
analysisNote.textContent = "This is a read-only query.";
}
}
window.datasetteSqlParameters.setupSqlParameterRefresh({
form,
url: form.dataset.analyzeUrl,
@ -262,9 +283,7 @@ window.addEventListener("DOMContentLoaded", () => {
if (submitButton) {
submitButton.disabled = data.save_disabled;
}
if (writableCheckbox) {
writableCheckbox.checked = data.analysis_is_write;
}
updateAnalysisNote(data);
},
onError(error) {
window.datasetteSqlAnalysis.renderAnalysis(analysisSection, {
@ -274,9 +293,7 @@ window.addEventListener("DOMContentLoaded", () => {
if (submitButton) {
submitButton.disabled = true;
}
if (writableCheckbox) {
writableCheckbox.checked = false;
}
updateAnalysisNote({ analysis_error: error.message });
},
});
});

View file

@ -990,6 +990,10 @@ async def test_create_query_ui_and_arbitrary_sql_save_link():
"/data/-/queries/insert?sql=select+*+from+dogs",
actor={"id": "root"},
)
write_create_response = await ds.client.get(
"/data/-/queries/insert?sql=insert+into+dogs+(name)+values+('Cleo')",
actor={"id": "root"},
)
blank_create_response = await ds.client.get(
"/data/-/queries/insert",
actor={"id": "root"},
@ -1005,7 +1009,6 @@ async def test_create_query_ui_and_arbitrary_sql_save_link():
assert create_response.status_code == 200
assert "Create query" in create_response.text
assert "Writable" in create_response.text
assert 'type="radio"' not in create_response.text
assert 'name="parameters"' not in create_response.text
assert 'id="query-parameters"' not in create_response.text
@ -1024,11 +1027,22 @@ async def test_create_query_ui_and_arbitrary_sql_save_link():
assert "renderParameters: false" in create_response.text
assert "datasetteSqlAnalysis.renderAnalysis" in create_response.text
assert "data-query-create-submit" in create_response.text
assert "data-query-create-writable" in create_response.text
assert "data-query-create-writable" not in create_response.text
assert "data-query-create-sql-type" not in create_response.text
assert "data-query-create-analysis-note" in create_response.text
assert "SQL type:" not in create_response.text
assert (
'<span class="query-create-analysis-note" data-query-create-analysis-note aria-live="polite">This is a read-only query.</span>'
in create_response.text
)
assert "disabled> Writable</label>" not in create_response.text
assert (
"Queries marked private can only be seen by you, their creator."
in create_response.text
)
assert create_response.text.index(
"This is a read-only query."
) < create_response.text.index('<input type="hidden" name="is_private" value="0">')
assert "<h2>Query operations</h2>" in create_response.text
assert '<table class="execute-write-analysis">' in create_response.text
assert '<th scope="col">Required permission</th>' in create_response.text
@ -1053,6 +1067,12 @@ async def test_create_query_ui_and_arbitrary_sql_save_link():
"<p>Analysis will show each affected table and required permission.</p>"
not in blank_create_response.text
)
assert "Enter SQL to analyze this query." in blank_create_response.text
assert write_create_response.status_code == 200
assert (
'<span class="query-create-analysis-note" data-query-create-analysis-note aria-live="polite">This query updates data in the database.</span>'
in write_create_response.text
)
assert query_response.status_code == 200
assert "Save this query" in query_response.text
assert "/data/-/queries/insert?sql=select+%2A+from+dogs" in query_response.text