mirror of
https://github.com/davidkaya/aryx.git
synced 2026-08-28 05:43:57 +02:00
fix(workflows): disable start/end palette buttons when node already exists
Prevents users from adding duplicate start or end nodes, which made the workflow invalid with no way to recover since those node kinds are protected from deletion. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -182,6 +182,13 @@ export function WorkflowEditor({
|
|||||||
|
|
||||||
const issues = validateWorkflowDefinition(activeWorkflow);
|
const issues = validateWorkflowDefinition(activeWorkflow);
|
||||||
|
|
||||||
|
const disabledPaletteKinds = useMemo(() => {
|
||||||
|
const disabled = new Set<WorkflowNodeKind>();
|
||||||
|
if (activeWorkflow.graph.nodes.some((n) => n.kind === 'start')) disabled.add('start');
|
||||||
|
if (activeWorkflow.graph.nodes.some((n) => n.kind === 'end')) disabled.add('end');
|
||||||
|
return disabled;
|
||||||
|
}, [activeWorkflow.graph.nodes]);
|
||||||
|
|
||||||
/* ── Change propagation ─────────────────────────────────── */
|
/* ── Change propagation ─────────────────────────────────── */
|
||||||
|
|
||||||
function propagateActiveChange(updatedActive: WorkflowDefinition) {
|
function propagateActiveChange(updatedActive: WorkflowDefinition) {
|
||||||
@@ -482,7 +489,7 @@ export function WorkflowEditor({
|
|||||||
<div className="flex min-h-0 flex-1">
|
<div className="flex min-h-0 flex-1">
|
||||||
{/* Left palette */}
|
{/* Left palette */}
|
||||||
<div className="w-40 shrink-0 overflow-y-auto border-r border-[var(--color-border)] bg-[var(--color-surface-1)]">
|
<div className="w-40 shrink-0 overflow-y-auto border-r border-[var(--color-border)] bg-[var(--color-surface-1)]">
|
||||||
<WorkflowNodePalette onAddNode={handleAddNode} />
|
<WorkflowNodePalette disabledKinds={disabledPaletteKinds} onAddNode={handleAddNode} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Center column: validation + canvas + settings */}
|
{/* Center column: validation + canvas + settings */}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type { WorkflowNodeKind } from '@shared/domain/workflow';
|
|||||||
|
|
||||||
interface WorkflowNodePaletteProps {
|
interface WorkflowNodePaletteProps {
|
||||||
onAddNode: (kind: WorkflowNodeKind) => void;
|
onAddNode: (kind: WorkflowNodeKind) => void;
|
||||||
|
disabledKinds?: ReadonlySet<WorkflowNodeKind>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PaletteItem {
|
interface PaletteItem {
|
||||||
@@ -48,7 +49,7 @@ const paletteGroups: PaletteGroup[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function WorkflowNodePalette({ onAddNode }: WorkflowNodePaletteProps) {
|
export function WorkflowNodePalette({ onAddNode, disabledKinds }: WorkflowNodePaletteProps) {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4 p-3">
|
<div className="space-y-4 p-3">
|
||||||
<h4 className="text-[10px] font-semibold uppercase tracking-wider text-[var(--color-text-muted)]">
|
<h4 className="text-[10px] font-semibold uppercase tracking-wider text-[var(--color-text-muted)]">
|
||||||
@@ -62,11 +63,18 @@ export function WorkflowNodePalette({ onAddNode }: WorkflowNodePaletteProps) {
|
|||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
{group.items.map((item) => {
|
{group.items.map((item) => {
|
||||||
const Icon = item.icon;
|
const Icon = item.icon;
|
||||||
|
const disabled = disabledKinds?.has(item.kind) ?? false;
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className="flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-left text-[12px] text-[var(--color-text-secondary)] transition-all duration-200 hover:bg-[var(--color-surface-3)] hover:text-[var(--color-text-primary)]"
|
className={`flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-left text-[12px] transition-all duration-200 ${
|
||||||
|
disabled
|
||||||
|
? 'cursor-not-allowed text-[var(--color-text-muted)] opacity-40'
|
||||||
|
: 'text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-3)] hover:text-[var(--color-text-primary)]'
|
||||||
|
}`}
|
||||||
|
disabled={disabled}
|
||||||
key={item.kind}
|
key={item.kind}
|
||||||
onClick={() => onAddNode(item.kind)}
|
onClick={() => onAddNode(item.kind)}
|
||||||
|
title={disabled ? `${item.label} node already exists` : undefined}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<Icon className={`size-3.5 ${item.color}`} />
|
<Icon className={`size-3.5 ${item.color}`} />
|
||||||
|
|||||||
Reference in New Issue
Block a user