fix(network-maps): address design critique — harden, normalize, clarify, polish
- Archive: two-step inline confirm in card dropdown menu - Delete Device/Edge: two-step inline confirm in PropertiesPanel footer - Context menu Delete: floating confirm bar instead of immediate deletion - AI Generate New: two-step confirm when replacing existing diagram nodes - DiagramHeader: show 'Unsaved changes' in amber when isDirty and not saving - deviceRegistry: SECURITY_COLOR #f97316 → #f87171 (deprecated ember orange removed) - CanvasEmptyPrompt: remove backdrop-blur (design system violation) - CanvasEmptyPrompt: remove redundant 'Skip AI' bottom button (duplicate of Build manually card) - CanvasEmptyPrompt: rounded-xl/rounded-2xl → rounded-lg, border-2 → border - Topology bar: h-1 → h-2 + native tooltip with category breakdown - AIAssistPanel: replace pulse-dot loading with spinner (consistent with rest of feature) - ContextMenu: add shadow-lg (consistent with other dropdowns) - DeviceNode tooltip: Position.Bottom → Position.Top (avoids canvas-edge clipping) - CanvasEmptyPrompt: raise ⌘↵ hint from /50 opacity to full text-muted-foreground Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useCallback, useState, useEffect } from 'react'
|
||||
import { Trash2, Minus, Spline, GitBranch } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import type { DeviceProperties, DiagramEdge } from '@/types'
|
||||
@@ -75,6 +75,11 @@ export function PropertiesPanel({
|
||||
onDeleteNode,
|
||||
onDeleteEdge,
|
||||
}: PropertiesPanelProps) {
|
||||
const [deleteConfirm, setDeleteConfirm] = useState(false)
|
||||
|
||||
// Reset confirm state whenever the selection changes
|
||||
useEffect(() => { setDeleteConfirm(false) }, [selectedNode?.id, selectedEdge?.id])
|
||||
|
||||
const handlePropertyChange = useCallback((field: keyof DeviceProperties, value: string) => {
|
||||
if (!selectedNode) return
|
||||
const nodeData = selectedNode.data as unknown as DeviceNodeData
|
||||
@@ -213,13 +218,33 @@ export function PropertiesPanel({
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t border-default p-3">
|
||||
<button
|
||||
onClick={() => onDeleteEdge(selectedEdge.id)}
|
||||
className="flex w-full items-center justify-center gap-1.5 rounded border border-red-500/30 px-2 py-1.5 text-xs text-red-400 hover:bg-red-500/10"
|
||||
>
|
||||
<Trash2 size={12} />
|
||||
Delete Connection
|
||||
</button>
|
||||
{deleteConfirm ? (
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<p className="text-center text-[10px] text-muted-foreground">Delete this connection?</p>
|
||||
<div className="flex gap-1.5">
|
||||
<button
|
||||
onClick={() => setDeleteConfirm(false)}
|
||||
className="flex-1 rounded border border-default px-2 py-1.5 text-xs text-primary hover:bg-elevated"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onDeleteEdge(selectedEdge.id)}
|
||||
className="flex-1 rounded bg-red-500/20 px-2 py-1.5 text-xs font-medium text-red-400 hover:bg-red-500/30"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
onClick={() => setDeleteConfirm(true)}
|
||||
className="flex w-full items-center justify-center gap-1.5 rounded border border-red-500/30 px-2 py-1.5 text-xs text-red-400 hover:bg-red-500/10"
|
||||
>
|
||||
<Trash2 size={12} />
|
||||
Delete Connection
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -326,13 +351,33 @@ export function PropertiesPanel({
|
||||
</div>
|
||||
|
||||
<div className="border-t border-default p-3">
|
||||
<button
|
||||
onClick={() => onDeleteNode(selectedNode!.id)}
|
||||
className="flex w-full items-center justify-center gap-1.5 rounded border border-red-500/30 px-2 py-1.5 text-xs text-red-400 hover:bg-red-500/10"
|
||||
>
|
||||
<Trash2 size={12} />
|
||||
Delete Device
|
||||
</button>
|
||||
{deleteConfirm ? (
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<p className="text-center text-[10px] text-muted-foreground">Delete this device?</p>
|
||||
<div className="flex gap-1.5">
|
||||
<button
|
||||
onClick={() => setDeleteConfirm(false)}
|
||||
className="flex-1 rounded border border-default px-2 py-1.5 text-xs text-primary hover:bg-elevated"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onDeleteNode(selectedNode!.id)}
|
||||
className="flex-1 rounded bg-red-500/20 px-2 py-1.5 text-xs font-medium text-red-400 hover:bg-red-500/30"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
onClick={() => setDeleteConfirm(true)}
|
||||
className="flex w-full items-center justify-center gap-1.5 rounded border border-red-500/30 px-2 py-1.5 text-xs text-red-400 hover:bg-red-500/10"
|
||||
>
|
||||
<Trash2 size={12} />
|
||||
Delete Device
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user