From 268ae7076fb4ff210ffe802f6e6ce8412789777f Mon Sep 17 00:00:00 2001 From: Arnas Donauskas Date: Fri, 22 May 2026 14:52:44 +0300 Subject: [PATCH] feat(flows): hide internal node_key + reply_id behind Advanced disclosure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Node key and per-button/row reply IDs are auto-generated and consumed only by the runner — exposing them in the main form read as developer leakage. Move them behind a per-node "Show advanced" toggle so the default UI shows only customer-facing fields. Power users who need stable IDs for analytics can still get them. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/components/flows/flow-builder.tsx | 129 ++++++++++++++++++-------- 1 file changed, 91 insertions(+), 38 deletions(-) diff --git a/src/components/flows/flow-builder.tsx b/src/components/flows/flow-builder.tsx index 095d097..23e00c2 100644 --- a/src/components/flows/flow-builder.tsx +++ b/src/components/flows/flow-builder.tsx @@ -873,21 +873,16 @@ function NodeConfigForm({ onUpdateConfig: (patch: Record) => void; }) { const cfg = node.config; + // Internal identifiers (node_key, reply_id columns on buttons/list rows) + // are auto-generated and the runner is the only consumer. Hide them by + // default so the form reads as plain editing; expose under "Advanced" + // for the rare case where someone wants to lock a key for stable + // analytics or external integration. + const [showAdvanced, setShowAdvanced] = useState(false); + const hasReplyIds = + node.node_type === "send_buttons" || node.node_type === "send_list"; return (
-
- - - onUpdate({ node_key: slugify(e.target.value, node.node_key) }) - } - className="bg-slate-800" - /> -
- {node.node_type === "start" && ( )} @@ -930,6 +926,7 @@ function NodeConfigForm({ allNodes={allNodes} currentKey={node.node_key} onUpdateConfig={onUpdateConfig} + showAdvanced={showAdvanced} /> )} @@ -1008,6 +1005,44 @@ function NodeConfigForm({ complete. No config needed.

)} + +
+ + {showAdvanced && ( +
+
+ + + onUpdate({ node_key: slugify(e.target.value, node.node_key) }) + } + className="bg-slate-800 font-mono text-xs" + /> +
+ {hasReplyIds && ( +

+ Reply IDs for each option are shown inline above. They're + returned by WhatsApp when a customer taps; you usually don't + need to touch them. +

+ )} +
+ )} +
); } @@ -1025,11 +1060,13 @@ function SendButtonsForm({ allNodes, currentKey, onUpdateConfig, + showAdvanced, }: { cfg: SendButtonsCfg; allNodes: BuilderNode[]; currentKey: string; onUpdateConfig: (patch: Record) => void; + showAdvanced: boolean; }) { const buttons = cfg.buttons ?? []; const updateButton = ( @@ -1077,18 +1114,25 @@ function SendButtonsForm({ {buttons.map((b, i) => (
- - updateButton(i, { - reply_id: slugify(e.target.value, `btn_${i + 1}`), - }) - } - placeholder="reply_id" - className="bg-slate-800 font-mono text-xs" - /> + {showAdvanced && ( + + updateButton(i, { + reply_id: slugify(e.target.value, `btn_${i + 1}`), + }) + } + placeholder="reply_id" + className="bg-slate-800 font-mono text-xs" + /> + )} updateButton(i, { title: e.target.value })} @@ -1152,11 +1196,13 @@ function SendListForm({ allNodes, currentKey, onUpdateConfig, + showAdvanced, }: { cfg: SendListCfg; allNodes: BuilderNode[]; currentKey: string; onUpdateConfig: (patch: Record) => void; + showAdvanced: boolean; }) { const sections = cfg.sections ?? []; const totalRows = sections.reduce((sum, s) => sum + s.rows.length, 0); @@ -1255,21 +1301,28 @@ function SendListForm({ {section.rows.map((row, rIdx) => (
- - updateRow(sIdx, rIdx, { - reply_id: slugify( - e.target.value, - `row_${rIdx + 1}`, - ), - }) - } - placeholder="reply_id" - className="bg-slate-800 font-mono text-xs" - /> + {showAdvanced && ( + + updateRow(sIdx, rIdx, { + reply_id: slugify( + e.target.value, + `row_${rIdx + 1}`, + ), + }) + } + placeholder="reply_id" + className="bg-slate-800 font-mono text-xs" + /> + )}