mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 17:29:45 +00:00
feat(flows): hide internal node_key + reply_id behind Advanced disclosure
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) <noreply@anthropic.com>
This commit is contained in:
parent
4cb6849291
commit
268ae7076f
@ -873,21 +873,16 @@ function NodeConfigForm({
|
|||||||
onUpdateConfig: (patch: Record<string, unknown>) => void;
|
onUpdateConfig: (patch: Record<string, unknown>) => void;
|
||||||
}) {
|
}) {
|
||||||
const cfg = node.config;
|
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 (
|
return (
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
<div>
|
|
||||||
<label className="mb-1 block text-xs text-slate-400">
|
|
||||||
Node key (used internally — keep stable)
|
|
||||||
</label>
|
|
||||||
<Input
|
|
||||||
value={node.node_key}
|
|
||||||
onChange={(e) =>
|
|
||||||
onUpdate({ node_key: slugify(e.target.value, node.node_key) })
|
|
||||||
}
|
|
||||||
className="bg-slate-800"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{node.node_type === "start" && (
|
{node.node_type === "start" && (
|
||||||
<NextNodeRow
|
<NextNodeRow
|
||||||
value={(cfg as { next_node_key?: string }).next_node_key ?? ""}
|
value={(cfg as { next_node_key?: string }).next_node_key ?? ""}
|
||||||
@ -921,6 +916,7 @@ function NodeConfigForm({
|
|||||||
allNodes={allNodes}
|
allNodes={allNodes}
|
||||||
currentKey={node.node_key}
|
currentKey={node.node_key}
|
||||||
onUpdateConfig={onUpdateConfig}
|
onUpdateConfig={onUpdateConfig}
|
||||||
|
showAdvanced={showAdvanced}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -930,6 +926,7 @@ function NodeConfigForm({
|
|||||||
allNodes={allNodes}
|
allNodes={allNodes}
|
||||||
currentKey={node.node_key}
|
currentKey={node.node_key}
|
||||||
onUpdateConfig={onUpdateConfig}
|
onUpdateConfig={onUpdateConfig}
|
||||||
|
showAdvanced={showAdvanced}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -1008,6 +1005,44 @@ function NodeConfigForm({
|
|||||||
complete. No config needed.
|
complete. No config needed.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<div className="border-t border-slate-800 pt-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowAdvanced((v) => !v)}
|
||||||
|
className="inline-flex items-center gap-1 text-xs text-slate-500 hover:text-slate-300"
|
||||||
|
>
|
||||||
|
{showAdvanced ? (
|
||||||
|
<ChevronUp className="h-3 w-3" />
|
||||||
|
) : (
|
||||||
|
<ChevronDown className="h-3 w-3" />
|
||||||
|
)}
|
||||||
|
{showAdvanced ? "Hide" : "Show"} advanced
|
||||||
|
</button>
|
||||||
|
{showAdvanced && (
|
||||||
|
<div className="mt-3 flex flex-col gap-3">
|
||||||
|
<div>
|
||||||
|
<label className="mb-1 block text-xs text-slate-400">
|
||||||
|
Node key (internal identifier — keep stable for analytics)
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
value={node.node_key}
|
||||||
|
onChange={(e) =>
|
||||||
|
onUpdate({ node_key: slugify(e.target.value, node.node_key) })
|
||||||
|
}
|
||||||
|
className="bg-slate-800 font-mono text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{hasReplyIds && (
|
||||||
|
<p className="text-[10px] text-slate-500">
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1025,11 +1060,13 @@ function SendButtonsForm({
|
|||||||
allNodes,
|
allNodes,
|
||||||
currentKey,
|
currentKey,
|
||||||
onUpdateConfig,
|
onUpdateConfig,
|
||||||
|
showAdvanced,
|
||||||
}: {
|
}: {
|
||||||
cfg: SendButtonsCfg;
|
cfg: SendButtonsCfg;
|
||||||
allNodes: BuilderNode[];
|
allNodes: BuilderNode[];
|
||||||
currentKey: string;
|
currentKey: string;
|
||||||
onUpdateConfig: (patch: Record<string, unknown>) => void;
|
onUpdateConfig: (patch: Record<string, unknown>) => void;
|
||||||
|
showAdvanced: boolean;
|
||||||
}) {
|
}) {
|
||||||
const buttons = cfg.buttons ?? [];
|
const buttons = cfg.buttons ?? [];
|
||||||
const updateButton = (
|
const updateButton = (
|
||||||
@ -1077,8 +1114,14 @@ function SendButtonsForm({
|
|||||||
{buttons.map((b, i) => (
|
{buttons.map((b, i) => (
|
||||||
<div
|
<div
|
||||||
key={i}
|
key={i}
|
||||||
className="grid grid-cols-1 gap-2 rounded-md border border-slate-800 bg-slate-800/40 p-3 md:grid-cols-[1fr_2fr_2fr_auto]"
|
className={cn(
|
||||||
|
"grid grid-cols-1 gap-2 rounded-md border border-slate-800 bg-slate-800/40 p-3",
|
||||||
|
showAdvanced
|
||||||
|
? "md:grid-cols-[1fr_2fr_2fr_auto]"
|
||||||
|
: "md:grid-cols-[2fr_2fr_auto]",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
|
{showAdvanced && (
|
||||||
<Input
|
<Input
|
||||||
value={b.reply_id}
|
value={b.reply_id}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@ -1089,6 +1132,7 @@ function SendButtonsForm({
|
|||||||
placeholder="reply_id"
|
placeholder="reply_id"
|
||||||
className="bg-slate-800 font-mono text-xs"
|
className="bg-slate-800 font-mono text-xs"
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
<Input
|
<Input
|
||||||
value={b.title}
|
value={b.title}
|
||||||
onChange={(e) => updateButton(i, { title: e.target.value })}
|
onChange={(e) => updateButton(i, { title: e.target.value })}
|
||||||
@ -1152,11 +1196,13 @@ function SendListForm({
|
|||||||
allNodes,
|
allNodes,
|
||||||
currentKey,
|
currentKey,
|
||||||
onUpdateConfig,
|
onUpdateConfig,
|
||||||
|
showAdvanced,
|
||||||
}: {
|
}: {
|
||||||
cfg: SendListCfg;
|
cfg: SendListCfg;
|
||||||
allNodes: BuilderNode[];
|
allNodes: BuilderNode[];
|
||||||
currentKey: string;
|
currentKey: string;
|
||||||
onUpdateConfig: (patch: Record<string, unknown>) => void;
|
onUpdateConfig: (patch: Record<string, unknown>) => void;
|
||||||
|
showAdvanced: boolean;
|
||||||
}) {
|
}) {
|
||||||
const sections = cfg.sections ?? [];
|
const sections = cfg.sections ?? [];
|
||||||
const totalRows = sections.reduce((sum, s) => sum + s.rows.length, 0);
|
const totalRows = sections.reduce((sum, s) => sum + s.rows.length, 0);
|
||||||
@ -1255,8 +1301,14 @@ function SendListForm({
|
|||||||
{section.rows.map((row, rIdx) => (
|
{section.rows.map((row, rIdx) => (
|
||||||
<div
|
<div
|
||||||
key={rIdx}
|
key={rIdx}
|
||||||
className="mb-2 grid grid-cols-1 gap-2 md:grid-cols-[1fr_2fr_2fr_auto]"
|
className={cn(
|
||||||
|
"mb-2 grid grid-cols-1 gap-2",
|
||||||
|
showAdvanced
|
||||||
|
? "md:grid-cols-[1fr_2fr_2fr_auto]"
|
||||||
|
: "md:grid-cols-[2fr_2fr_auto]",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
|
{showAdvanced && (
|
||||||
<Input
|
<Input
|
||||||
value={row.reply_id}
|
value={row.reply_id}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@ -1270,6 +1322,7 @@ function SendListForm({
|
|||||||
placeholder="reply_id"
|
placeholder="reply_id"
|
||||||
className="bg-slate-800 font-mono text-xs"
|
className="bg-slate-800 font-mono text-xs"
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
<Input
|
<Input
|
||||||
value={row.title}
|
value={row.title}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user