From 3f96f4f81bc1ee76f909e45ab2f5e6ea11ba6a34 Mon Sep 17 00:00:00 2001 From: clawbot Date: Tue, 10 Mar 2026 11:23:36 -0700 Subject: [PATCH] fix: match original table UI with immediate per-action submission MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the Save All workflow with the original per-action behavior: - Edit row: shows Save/Cancel buttons, submits full set immediately - Delete row: shows confirmation dialog, submits full set immediately - Add row: submits full set immediately on Add click Moves Alpine.js logic into a proper envVarEditor component in app-detail.js. Initializes env var data from hidden span elements with data attributes for safe HTML escaping. All actions collect the complete env var set and POST to the single bulk endpoint POST /apps/{id}/env — no Save All button needed. --- static/js/app-detail.js | 62 ++++++++++++++++++ templates/app_detail.html | 134 +++++++++++++++----------------------- 2 files changed, 113 insertions(+), 83 deletions(-) diff --git a/static/js/app-detail.js b/static/js/app-detail.js index 9778ea8..8636d54 100644 --- a/static/js/app-detail.js +++ b/static/js/app-detail.js @@ -6,6 +6,68 @@ */ document.addEventListener("alpine:init", () => { + // ============================================ + // Environment Variable Editor Component + // ============================================ + Alpine.data("envVarEditor", () => ({ + vars: [], + editIdx: -1, + editKey: "", + editVal: "", + + init() { + this.vars = Array.from(this.$el.querySelectorAll(".env-init")).map( + (span) => ({ + key: span.dataset.key, + value: span.dataset.value, + }), + ); + }, + + startEdit(i) { + this.editIdx = i; + this.editKey = this.vars[i].key; + this.editVal = this.vars[i].value; + }, + + saveEdit(i) { + this.vars[i] = { key: this.editKey, value: this.editVal }; + this.editIdx = -1; + this.submitAll(); + }, + + removeVar(i) { + if (!window.confirm("Delete this environment variable?")) { + return; + } + + this.vars.splice(i, 1); + this.submitAll(); + }, + + addVar(keyEl, valEl) { + const k = keyEl.value.trim(); + const v = valEl.value.trim(); + + if (!k) { + return; + } + + this.vars.push({ key: k, value: v }); + this.submitAll(); + }, + + submitAll() { + this.$refs.bulkData.value = this.vars + .map((e) => e.key + "=" + e.value) + .join("\n"); + this.$refs.bulkForm.submit(); + }, + })); + + // ============================================ + // App Detail Page Component + // ============================================ Alpine.data("appDetail", (config) => ({ appId: config.appId, currentDeploymentId: config.initialDeploymentId, diff --git a/templates/app_detail.html b/templates/app_detail.html index e315e4c..0e10cf9 100644 --- a/templates/app_detail.html +++ b/templates/app_detail.html @@ -101,91 +101,59 @@ -
+

Environment Variables

-
+ {{range .EnvVars}}{{end}} + + + + + +
+