docs: add network diagrams design spec
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
499
docs/superpowers/specs/2026-04-04-network-diagrams-design.md
Normal file
499
docs/superpowers/specs/2026-04-04-network-diagrams-design.md
Normal file
@@ -0,0 +1,499 @@
|
||||
# Network Diagrams — Design Spec
|
||||
|
||||
> **Date:** 2026-04-04
|
||||
> **Status:** Approved
|
||||
> **Scope:** Standalone network diagram builder (Phase 1 — no FlowPilot integration)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
A fully functional Network Diagram builder for MSP engineers. Engineers can manually build network topology diagrams with drag-and-drop device nodes and connections, or use AI to generate diagrams from plain English descriptions. Diagrams are team-scoped and filterable by client.
|
||||
|
||||
**User-facing label:** "Network Maps" in the sidebar.
|
||||
|
||||
---
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
| Decision | Choice | Rationale |
|
||||
|----------|--------|-----------|
|
||||
| Device types | Database-driven with system defaults | MSPs serve different verticals with different gear. Extensibility from day one avoids a refactor. |
|
||||
| Device type rendering | DB stores identity + metadata; frontend maps icons/colors by slug | Clean separation. Custom types get category-based default icons. Icon picker deferred to future. |
|
||||
| Connection types | Fixed defaults + free-text "Custom..." option | Connection types are more standardized than devices. 6 defaults cover 95% of cases. |
|
||||
| AI generation | Replace + Merge (Add to Diagram) modes | Iterative building is the natural workflow: "generate base" then "add VPN to branch office." |
|
||||
| Export formats | PNG + PDF + JSON | PNG for sharing, PDF for formal client docs, JSON for backup/import/portability. |
|
||||
| Client filter | Combobox/autocomplete | Type to filter, click to select from distinct client_name values. |
|
||||
| State management | Local React state (not Zustand) | Matches AssistantChatPage pattern. Editor page owns nodes/edges/meta. |
|
||||
| Scope | Standalone tool only | Future integration with FlowPilot sessions documented but not built. |
|
||||
|
||||
---
|
||||
|
||||
## Data Model
|
||||
|
||||
### `device_types` table
|
||||
|
||||
Stores system-default and team-custom device types.
|
||||
|
||||
```sql
|
||||
CREATE TABLE device_types (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
slug VARCHAR(50) NOT NULL,
|
||||
label VARCHAR(100) NOT NULL,
|
||||
category VARCHAR(50) NOT NULL, -- network, compute, storage, cloud, endpoint, infrastructure, security
|
||||
is_system BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
team_id UUID REFERENCES teams(id) ON DELETE CASCADE,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
CONSTRAINT uq_device_types_slug_team UNIQUE NULLS NOT DISTINCT (slug, team_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_device_types_team ON device_types(team_id);
|
||||
```
|
||||
|
||||
- `is_system = true, team_id = NULL` for built-in types
|
||||
- `is_system = false, team_id = <uuid>` for team-custom types
|
||||
- `slug` is the key used in diagram node data and the frontend rendering registry
|
||||
|
||||
### System device type seed data
|
||||
|
||||
| Slug | Label | Category |
|
||||
|------|-------|----------|
|
||||
| `router` | Router | network |
|
||||
| `switch` | Switch | network |
|
||||
| `firewall` | Firewall | network |
|
||||
| `access-point` | Access Point | network |
|
||||
| `load-balancer` | Load Balancer | network |
|
||||
| `server` | Server | compute |
|
||||
| `workstation` | Workstation | compute |
|
||||
| `vm` | Virtual Machine | compute |
|
||||
| `container` | Container | compute |
|
||||
| `nas` | NAS | storage |
|
||||
| `san` | SAN | storage |
|
||||
| `cloud-storage` | Cloud Storage | storage |
|
||||
| `cloud` | Cloud | cloud |
|
||||
| `aws` | AWS | cloud |
|
||||
| `azure` | Azure | cloud |
|
||||
| `gcp` | Google Cloud | cloud |
|
||||
| `printer` | Printer | endpoint |
|
||||
| `phone` | Phone | endpoint |
|
||||
| `iot` | IoT Device | endpoint |
|
||||
| `camera` | Camera | endpoint |
|
||||
| `tablet` | Tablet | endpoint |
|
||||
| `laptop` | Laptop | endpoint |
|
||||
| `ups` | UPS | infrastructure |
|
||||
| `pdu` | PDU | infrastructure |
|
||||
| `rack` | Rack | infrastructure |
|
||||
| `patch-panel` | Patch Panel | infrastructure |
|
||||
| `nvr` | NVR | security |
|
||||
| `badge-reader` | Badge Reader | security |
|
||||
|
||||
### `network_diagrams` table
|
||||
|
||||
```sql
|
||||
CREATE TABLE network_diagrams (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
client_name VARCHAR(255),
|
||||
asset_name VARCHAR(255),
|
||||
description TEXT,
|
||||
nodes JSONB NOT NULL DEFAULT '[]',
|
||||
edges JSONB NOT NULL DEFAULT '[]',
|
||||
thumbnail_url TEXT,
|
||||
is_archived BOOLEAN DEFAULT FALSE,
|
||||
created_by UUID REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_network_diagrams_team ON network_diagrams(team_id);
|
||||
CREATE INDEX idx_network_diagrams_client ON network_diagrams(team_id, client_name);
|
||||
```
|
||||
|
||||
### JSONB Node Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "unique-string",
|
||||
"type": "router",
|
||||
"label": "Core Router",
|
||||
"position": { "x": 400, "y": 200 },
|
||||
"properties": {
|
||||
"hostname": "core-rtr-01",
|
||||
"ip": "10.0.0.1",
|
||||
"subnet": "10.0.0.0/24",
|
||||
"vendor": "Cisco",
|
||||
"model": "ISR 4331",
|
||||
"role": "Core gateway",
|
||||
"vlan": "1",
|
||||
"notes": "",
|
||||
"status": "online"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `type` field stores the device type `slug` (string, not enum)
|
||||
- `status` is one of: `unknown`, `online`, `offline`, `degraded`
|
||||
|
||||
### JSONB Edge Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "unique-string",
|
||||
"source": "node-id",
|
||||
"target": "node-id",
|
||||
"label": "Uplink to Core",
|
||||
"connectionType": "ethernet",
|
||||
"speed": "1 Gbps",
|
||||
"notes": "Port Gi0/1 -> Gi0/24"
|
||||
}
|
||||
```
|
||||
|
||||
- `connectionType` is a free string. Standard defaults: `ethernet`, `fiber`, `wifi`, `vpn`, `vlan`, `wan`
|
||||
- `speed` and `notes` are additional edge metadata fields (not in original spec, added for MSP usefulness)
|
||||
|
||||
### JSON Export Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"name": "Office Network",
|
||||
"client_name": "Acme Corp",
|
||||
"description": "Main office topology",
|
||||
"nodes": [...],
|
||||
"edges": [...],
|
||||
"exportedAt": "2026-04-04T12:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Device Types
|
||||
|
||||
All endpoints require authentication via `get_current_active_user`.
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| `GET` | `/api/v1/device-types/` | List all (system + team custom), ordered by category then sort_order |
|
||||
| `POST` | `/api/v1/device-types/` | Create custom type for team |
|
||||
| `PUT` | `/api/v1/device-types/{id}` | Update custom type (team-owned only) |
|
||||
| `DELETE` | `/api/v1/device-types/{id}` | Delete custom type (team-owned only, fails if `is_system`) |
|
||||
|
||||
### Network Diagrams
|
||||
|
||||
All endpoints team-scoped via `get_current_active_user`. Team ownership verified on every single-resource operation.
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| `POST` | `/api/v1/network-diagrams/` | Create diagram |
|
||||
| `GET` | `/api/v1/network-diagrams/` | List for team (exclude archived). Query params: `client_name`, `search` |
|
||||
| `GET` | `/api/v1/network-diagrams/{id}` | Get single diagram |
|
||||
| `PUT` | `/api/v1/network-diagrams/{id}` | Update diagram |
|
||||
| `DELETE` | `/api/v1/network-diagrams/{id}` | Soft delete (set `is_archived=True`) |
|
||||
| `POST` | `/api/v1/network-diagrams/{id}/duplicate` | Duplicate diagram (new name: "Copy of {name}") |
|
||||
| `POST` | `/api/v1/network-diagrams/import` | Import from JSON file |
|
||||
| `GET` | `/api/v1/network-diagrams/{id}/export` | Export as JSON |
|
||||
| `POST` | `/api/v1/network-diagrams/ai-generate` | AI diagram generation |
|
||||
| `GET` | `/api/v1/network-diagrams/clients` | List distinct client_name values for team (powers combobox) |
|
||||
|
||||
### AI Generate Endpoint
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"description": "Small office with a firewall, core switch, 3 access points, and a file server",
|
||||
"client_name": "Acme Corp",
|
||||
"mode": "replace",
|
||||
"existingBounds": null
|
||||
}
|
||||
```
|
||||
|
||||
For merge mode:
|
||||
```json
|
||||
{
|
||||
"description": "Add a VPN tunnel to a branch office with 2 workstations",
|
||||
"client_name": "Acme Corp",
|
||||
"mode": "merge",
|
||||
"existingBounds": { "minX": 0, "maxX": 800, "minY": 0, "maxY": 600 }
|
||||
}
|
||||
```
|
||||
|
||||
**System prompt** includes:
|
||||
- The generation rules from the original spec (JSON-only response, topology layout guidelines)
|
||||
- The list of available device type slugs (system + team custom)
|
||||
- For merge mode: existing node positions and instruction to place new nodes in available space
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"nodes": [...],
|
||||
"edges": [...],
|
||||
"suggestedName": "Acme Corp - Main Office",
|
||||
"notes": "Assumed site-to-site VPN for branch office connection"
|
||||
}
|
||||
```
|
||||
|
||||
**Error handling:**
|
||||
- JSON parse failure: 422 "AI generated an invalid response, please try again"
|
||||
- Unknown device type slug: silently fall back to closest category match
|
||||
- Timeout: 504 "Generation took too long"
|
||||
|
||||
---
|
||||
|
||||
## Frontend Architecture
|
||||
|
||||
### New Files
|
||||
|
||||
```
|
||||
frontend/src/
|
||||
├── api/
|
||||
│ ├── deviceTypes.ts # Device types API client
|
||||
│ └── networkDiagrams.ts # Network diagrams API client
|
||||
├── components/network/
|
||||
│ ├── nodes/
|
||||
│ │ ├── deviceRegistry.ts # Slug → { icon, color } mapping + category defaults
|
||||
│ │ ├── DeviceNode.tsx # React Flow custom node
|
||||
│ │ └── nodeTypes.ts # React Flow node type registry
|
||||
│ ├── edges/
|
||||
│ │ └── ConnectionEdge.tsx # Custom edge with connection-type styling
|
||||
│ ├── panels/
|
||||
│ │ ├── DeviceToolbar.tsx # Left panel — categorized, searchable, draggable
|
||||
│ │ ├── PropertiesPanel.tsx # Right panel — node/edge property editor
|
||||
│ │ └── AIAssistPanel.tsx # Bottom collapsible — AI generation
|
||||
│ ├── NetworkCanvas.tsx # React Flow wrapper
|
||||
│ └── DiagramHeader.tsx # Top bar — name, save, export
|
||||
├── pages/NetworkDiagrams/
|
||||
│ ├── index.tsx # List/dashboard page
|
||||
│ └── DiagramEditor.tsx # Full editor page (assembles all panels)
|
||||
└── types/
|
||||
└── networkDiagram.ts # TypeScript interfaces
|
||||
```
|
||||
|
||||
### Modified Files
|
||||
|
||||
- `frontend/src/components/layout/Sidebar.tsx` — add "Network Maps" nav item
|
||||
- `frontend/src/router.tsx` — add routes
|
||||
- `frontend/src/api/index.ts` — export new API modules
|
||||
- `frontend/src/types/index.ts` — export new types
|
||||
- `backend/app/api/router.py` — register new routers
|
||||
- `backend/app/models/__init__.py` — export new models (if pattern requires)
|
||||
|
||||
### Page Layouts
|
||||
|
||||
**List Page** (`/network-diagrams`)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ Network Maps [Import] [New Diagram]│
|
||||
│ Visual network topology documentation for your clients │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ [🔍 Search diagrams...] [Client ▾ combobox filter] │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ Diagram │ │ Diagram │ │ Diagram │ │
|
||||
│ │ Card │ │ Card │ │ Card │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ name │ │ name │ │ name │ │
|
||||
│ │ client │ │ client │ │ client │ │
|
||||
│ │ 12 devs │ │ 8 devs │ │ 5 devs │ │
|
||||
│ │ ⋯ menu │ │ ⋯ menu │ │ ⋯ menu │ │
|
||||
│ └──────────┘ └──────────┘ └──────────┘ │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Card three-dot menu: Open, Duplicate, Export JSON, Export PNG, Export PDF, Archive
|
||||
|
||||
**Editor Page** (`/network-diagrams/new`, `/network-diagrams/:id`)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ ← Network Maps │ Diagram Name [Acme Corp] │ Save │ Export│
|
||||
├──────────┬──────────────────────────────┬───────────────────┤
|
||||
│ [🔍] │ │ Properties Panel │
|
||||
│ Network │ │ (when selected) │
|
||||
│ ├ Router│ React Flow Canvas │ │
|
||||
│ ├ Switch│ (dot background) │ Hostname [____] │
|
||||
│ └ ... │ (minimap bottom-right) │ IP [____] │
|
||||
│ Compute │ (controls top-left) │ Subnet [____] │
|
||||
│ ├ Server│ │ Vendor [____] │
|
||||
│ └ ... │ │ ... │
|
||||
│ ... │ │ │
|
||||
│ [+Custom│ │ [Delete Device] │
|
||||
│ Type] │ │ │
|
||||
├──────────┴──────────────────────────────┴───────────────────┤
|
||||
│ [✨ AI Generate] (collapsed) ──or── expanded textarea │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Component Details
|
||||
|
||||
**DeviceToolbar (left, 200px)**
|
||||
- Search box at top — filters across all categories
|
||||
- Collapsible category sections (Network, Compute, Storage, Cloud, Endpoint, Infrastructure, Security)
|
||||
- Each device: draggable card with icon + label
|
||||
- HTML5 drag-and-drop — `onDragStart` sets device type slug in dataTransfer
|
||||
- "+ Custom Type" button at bottom — opens inline form (slug, label, category dropdown)
|
||||
- Fetches device types from API on mount
|
||||
- `bg-sidebar` background, `border-default` right border
|
||||
|
||||
**DeviceNode (React Flow custom node)**
|
||||
- Renders device icon from registry (resolved by slug)
|
||||
- Label below icon
|
||||
- IP address below label (JetBrains Mono, muted color) if set
|
||||
- Status dot: green=online, red=offline, yellow=degraded, gray=unknown
|
||||
- Connection handles (top, bottom, left, right) — visible on hover
|
||||
- Selected: border changes to accent blue
|
||||
- `bg-card`, `border-default`, 8px radius, min-width 120px
|
||||
|
||||
**ConnectionEdge (React Flow custom edge)**
|
||||
- Renders as smoothstep edge
|
||||
- Visual style varies by connectionType:
|
||||
- `ethernet`: solid blue line
|
||||
- `fiber`: solid green, slightly thicker
|
||||
- `wifi`: dotted purple
|
||||
- `vpn`: dashed yellow
|
||||
- `vlan`: solid gray
|
||||
- `wan`: dashed red
|
||||
- custom: solid default with label
|
||||
- Shows connection label on the edge
|
||||
|
||||
**PropertiesPanel (right, 260px)**
|
||||
- **Node selected**: hostname, IP, subnet, vendor, model, role, VLAN, notes (text inputs), status (styled dropdown)
|
||||
- **Edge selected**: label, connectionType (dropdown with "Custom..." option), speed, notes
|
||||
- **Nothing selected**: "Select a device to edit its properties"
|
||||
- Changes update node/edge data immediately (controlled inputs)
|
||||
- "Delete" button at bottom (red accent)
|
||||
- `bg-sidebar`, `border-default` left border
|
||||
|
||||
**AIAssistPanel (bottom, collapsible)**
|
||||
- Collapsed: "AI Generate" button in a slim bar
|
||||
- Expanded: textarea, mode toggle ("Generate New" / "Add to Diagram"), "Generate" button
|
||||
- Replace mode warning: "This will replace your current diagram. Save first if needed."
|
||||
- Loading: animated pulse + "Generating your network diagram..."
|
||||
- Success: replaces/merges canvas, shows toast with suggested name
|
||||
- Error: inline error message
|
||||
|
||||
**DiagramHeader (top, 56px)**
|
||||
- Back button: `← Network Maps` navigates to list page
|
||||
- Inline-editable diagram name (click to edit, Enter to confirm)
|
||||
- Client name badge (pill) if set
|
||||
- Save button (primary, "Saving..." state)
|
||||
- Export dropdown: PNG, PDF, JSON
|
||||
- Last saved timestamp (muted, right side)
|
||||
- `bg-card`, `border-default` bottom border
|
||||
|
||||
### State Management
|
||||
|
||||
`DiagramEditor` page owns all state via `useState`:
|
||||
|
||||
- `nodes` / `edges` — managed by React Flow's `useNodesState` / `useEdgesState`
|
||||
- `diagramMeta` — name, client_name, asset_name, description
|
||||
- `isDirty` — tracks unsaved changes
|
||||
- `lastSavedAt` — timestamp for header display
|
||||
- `selectedNode` / `selectedEdge` — drives PropertiesPanel
|
||||
|
||||
**Auto-save**: `setInterval` at 30 seconds, only fires if `isDirty` is true. Clears dirty flag on save. Does not fire for new unsaved diagrams (must explicitly save first to create the record).
|
||||
|
||||
### Export Implementation
|
||||
|
||||
- **PNG**: Client-side using the Canvas API. Render the React Flow viewport to an offscreen canvas via `ReactFlow.toObject()` + manual SVG-to-canvas rendering, then `canvas.toBlob()` for download. No external library needed.
|
||||
- **PDF**: Client-side using `window.print()` with a print-specific stylesheet. Opens a print-optimized view of the diagram with metadata header (name, client, description, date, device count) and device legend. The user selects "Save as PDF" in the browser print dialog. No external library needed.
|
||||
- **JSON**: API call to `GET /network-diagrams/{id}/export`, triggers file download
|
||||
|
||||
### Import Implementation
|
||||
|
||||
- "Import" button on list page opens file picker (`.json` only)
|
||||
- Uploads to `POST /network-diagrams/import`
|
||||
- Backend validates schema version and structure
|
||||
- Creates new diagram for the team
|
||||
- Response includes warnings for unknown device type slugs
|
||||
- On success, navigates to the new diagram's editor
|
||||
|
||||
---
|
||||
|
||||
## Navigation Integration
|
||||
|
||||
### Sidebar
|
||||
|
||||
Add "Network Maps" to both sidebar modes:
|
||||
|
||||
**Rail groups (icon-only mode)**: Add as a child under the Flows group:
|
||||
```typescript
|
||||
{
|
||||
href: '/trees', icon: GitBranch, label: 'Flows', shortLabel: 'Flows',
|
||||
children: [
|
||||
{ href: '/trees', label: 'Flow Library' },
|
||||
{ href: '/trees?type=procedural', label: 'Projects' },
|
||||
{ href: '/network-diagrams', label: 'Network Maps' }, // NEW
|
||||
{ href: '/step-library', label: 'Solutions Library' },
|
||||
{ href: '/review-queue', label: 'Review Queue' },
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
**Pinned sections**: Add under KNOWLEDGE:
|
||||
```typescript
|
||||
{
|
||||
title: 'KNOWLEDGE',
|
||||
items: [
|
||||
{ href: '/trees', icon: GitBranch, label: 'Flow Library', ... },
|
||||
{ href: '/network-diagrams', icon: Network, label: 'Network Maps', shortLabel: 'NetMap' }, // NEW
|
||||
{ href: '/scripts', icon: Code2, label: 'Scripts', ... },
|
||||
...
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
### Routes
|
||||
|
||||
```typescript
|
||||
const NetworkDiagramsPage = lazyWithRetry(() => import('@/pages/NetworkDiagrams'))
|
||||
const DiagramEditorPage = lazyWithRetry(() => import('@/pages/NetworkDiagrams/DiagramEditor'))
|
||||
|
||||
// Inside ProtectedRoute/AppLayout children:
|
||||
{ path: 'network-diagrams', element: page(NetworkDiagramsPage) },
|
||||
{ path: 'network-diagrams/new', element: page(DiagramEditorPage) },
|
||||
{ path: 'network-diagrams/:id', element: page(DiagramEditorPage) },
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Future Integration Roadmap
|
||||
|
||||
These features are documented for future implementation. They are NOT in scope for Phase 1.
|
||||
|
||||
### Phase 2: FlowPilot Context Integration
|
||||
When a FlowPilot session has a `client_name` matching a saved network diagram, the diagram can be surfaced as read-only reference context. The AI copilot gains topology awareness — it can reference specific devices, connections, and network segments when guiding troubleshooting.
|
||||
|
||||
### Phase 3: Live Status Overlay
|
||||
Device status indicators updated from PSA/monitoring integrations (ConnectWise, Datto RMM, etc.). Green/red/yellow dots reflect real-time device state. Engineers see at-a-glance which devices are down before starting troubleshooting.
|
||||
|
||||
### Phase 4: Session-Linked Diagrams
|
||||
Attach a diagram to a FlowPilot session. During troubleshooting, highlight the device being investigated. Mark devices as "checked" or "problem found." Session resolution includes a diagram snapshot showing the problem path.
|
||||
|
||||
### Phase 5: Custom Device Type Icons
|
||||
Icon picker UI for team-custom device types. Optional SVG upload for vendor logos or specialized equipment icons (with SVG sanitization for security).
|
||||
|
||||
### Phase 6: Diagram Templates
|
||||
Pre-built topology templates as starting points: Small Office, Branch + HQ, Data Center, Remote Worker, MSP NOC. Templates include placeholder devices with typical connections.
|
||||
|
||||
### Phase 7: Collaborative Editing
|
||||
Multiple engineers editing the same diagram simultaneously. Requires WebSocket infrastructure (Yjs or similar CRDT). Cursor presence, conflict resolution, real-time sync.
|
||||
|
||||
---
|
||||
|
||||
## Quality Requirements
|
||||
|
||||
- Every file fully typed — no `any`, no untyped props
|
||||
- No inline styles — Tailwind classes only, using design system CSS variable tokens
|
||||
- No placeholder TODOs — build the real implementation
|
||||
- All API calls handle loading, error, and success states
|
||||
- Team-scoping enforced on every backend endpoint
|
||||
- Ownership verification on every GET/PUT/DELETE by ID
|
||||
- Editor handles both "new diagram" and "edit existing" modes
|
||||
- Auto-save only fires when dirty
|
||||
- No new npm packages — use only what's installed
|
||||
- Follow existing code style exactly
|
||||
Reference in New Issue
Block a user