feat: add manual create option for network maps

This commit is contained in:
chihlasm
2026-04-12 05:26:37 +00:00
parent 3c2b1dd16e
commit 4527571d5f

View File

@@ -1,5 +1,5 @@
import { useState, useCallback } from 'react'
import { Sparkles, ArrowRight } from 'lucide-react'
import { Sparkles, ArrowRight, PencilRuler, Wand2 } from 'lucide-react'
import { networkDiagramsApi } from '@/api'
import type { AIGenerateResponse } from '@/types'
@@ -16,6 +16,7 @@ interface CanvasEmptyPromptProps {
}
export function CanvasEmptyPrompt({ onGenerate }: CanvasEmptyPromptProps) {
const [mode, setMode] = useState<'choice' | 'ai' | 'manual'>('choice')
const [description, setDescription] = useState('')
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
@@ -39,9 +40,72 @@ export function CanvasEmptyPrompt({ onGenerate }: CanvasEmptyPromptProps) {
}
}, [description, onGenerate])
if (mode === 'manual') {
return (
<div className="pointer-events-none absolute inset-x-0 bottom-6 z-10 flex justify-center px-6">
<div className="pointer-events-auto flex items-center gap-3 rounded-full border border-default bg-card/95 px-4 py-2 shadow-xl backdrop-blur">
<span className="text-xs text-muted-foreground">
Manual mode enabled. Drag devices from the left panel to start building.
</span>
<button
onClick={() => setMode('ai')}
className="inline-flex items-center gap-1 rounded-full border border-default px-3 py-1 text-xs font-medium text-primary hover:border-accent hover:text-accent"
>
<Sparkles size={12} />
Open AI Generator
</button>
</div>
</div>
)
}
return (
<div className="pointer-events-none absolute inset-0 z-10 flex items-center justify-center">
<div className="pointer-events-auto w-full max-w-lg rounded-xl border border-default bg-card p-8 shadow-2xl">
{mode === 'choice' ? (
<>
<div className="mb-6 text-center">
<div className="mb-2 flex items-center justify-center gap-2">
<Wand2 size={16} className="text-accent" />
<h2 className="font-heading text-base font-semibold text-heading">
Start a network map
</h2>
</div>
<p className="text-xs text-muted-foreground">
Generate a topology with AI or start with a blank canvas and build it manually.
</p>
</div>
<div className="grid gap-3 sm:grid-cols-2">
<button
onClick={() => setMode('ai')}
className="rounded-xl border border-accent/40 bg-accent/10 p-4 text-left transition-colors hover:border-accent hover:bg-accent/15"
>
<div className="mb-3 inline-flex rounded-lg bg-accent/15 p-2 text-accent">
<Sparkles size={16} />
</div>
<div className="mb-1 text-sm font-semibold text-heading">Generate with AI</div>
<p className="text-xs text-muted-foreground">
Describe the environment and let AI lay out the first version for you.
</p>
</button>
<button
onClick={() => setMode('manual')}
className="rounded-xl border border-default bg-elevated/30 p-4 text-left transition-colors hover:border-hover hover:bg-elevated/50"
>
<div className="mb-3 inline-flex rounded-lg bg-elevated p-2 text-primary">
<PencilRuler size={16} />
</div>
<div className="mb-1 text-sm font-semibold text-heading">Build manually</div>
<p className="text-xs text-muted-foreground">
Close the AI prompt and drag devices from the left panel onto the canvas.
</p>
</button>
</div>
</>
) : (
<>
<div className="mb-5 text-center">
<div className="mb-2 flex items-center justify-center gap-2">
<Sparkles size={16} className="text-accent" />
@@ -50,7 +114,7 @@ export function CanvasEmptyPrompt({ onGenerate }: CanvasEmptyPromptProps) {
</h2>
</div>
<p className="text-xs text-muted-foreground">
AI will generate the topology in seconds or drag devices from the left panel to build manually.
AI will generate the topology in seconds, or you can go back and switch to manual creation.
</p>
</div>
@@ -87,8 +151,19 @@ export function CanvasEmptyPrompt({ onGenerate }: CanvasEmptyPromptProps) {
{error && <p className="mb-3 text-xs text-red-400">{error}</p>}
<div className="flex gap-2">
<button
onClick={() => {
setMode('manual')
setError(null)
}}
disabled={loading}
className="flex-1 rounded-lg border border-default px-4 py-2.5 text-sm font-medium text-primary hover:border-hover disabled:opacity-40"
>
Build Manually
</button>
{loading ? (
<div className="flex items-center justify-center gap-2 py-2.5">
<div className="flex flex-1 items-center justify-center gap-2 py-2.5">
<div className="h-4 w-4 animate-spin rounded-full border-2 border-accent border-t-transparent" />
<span className="text-sm text-muted-foreground">Mapping your network</span>
</div>
@@ -96,7 +171,7 @@ export function CanvasEmptyPrompt({ onGenerate }: CanvasEmptyPromptProps) {
<button
onClick={() => handleGenerate()}
disabled={!description.trim()}
className="flex w-full items-center justify-center gap-2 rounded-lg bg-accent px-4 py-2.5 text-sm font-medium text-white transition-opacity hover:bg-accent/90 disabled:opacity-40"
className="flex flex-1 items-center justify-center gap-2 rounded-lg bg-accent px-4 py-2.5 text-sm font-medium text-white transition-opacity hover:bg-accent/90 disabled:opacity-40"
>
<Sparkles size={14} />
Generate Diagram
@@ -104,6 +179,9 @@ export function CanvasEmptyPrompt({ onGenerate }: CanvasEmptyPromptProps) {
</button>
)}
</div>
</>
)}
</div>
</div>
)
}