# @x025me/link-analysis

A beautiful, interactive React component for visualizing network graphs and relationships. Perfect for displaying connections between people, organizations, phone numbers, or any linked data.

## 🚀 Quick Start

### Step 1: Install

```bash
npm install @x025me/link-analysis react react-dom
```

### Step 2: Add Styles

In your main entry file (usually `main.tsx` or `index.tsx`):

```tsx
import 'antd/dist/reset.css'
```

### Step 3: Use the Component

```tsx
import { GraphVisualization } from '@x025me/link-analysis'

function App() {
  const graphData = {
    nodes: [
      { id: '1', label: 'Alice', type: 'person' },
      { id: '2', label: 'Bob', type: 'person' },
      { id: '3', label: 'Company Inc', type: 'organization' },
    ],
    edges: [
      { id: 'e1', source: '1', target: '2', label: 'knows' },
      { id: 'e2', source: '1', target: '3', label: 'works at' },
    ],
  }

  return (
    <div style={{ width: '100%', height: '600px' }}>
      <GraphVisualization data={graphData} />
    </div>
  )
}
```

That's it! You now have an interactive graph visualization.

## 📖 Common Use Cases

### 1. Displaying Social Networks

Show relationships between people:

```tsx
import { GraphVisualization } from '@x025me/link-analysis'

const socialNetwork = {
  nodes: [
    { id: 'alice', label: 'Alice', type: 'person' },
    { id: 'bob', label: 'Bob', type: 'person' },
    { id: 'charlie', label: 'Charlie', type: 'person' },
  ],
  edges: [
    { id: 'e1', source: 'alice', target: 'bob', label: 'friends' },
    { id: 'e2', source: 'bob', target: 'charlie', label: 'friends' },
  ],
}

function SocialNetwork() {
  return (
    <div style={{ width: '100%', height: '500px' }}>
      <GraphVisualization data={socialNetwork} />
    </div>
  )
}
```

### 2. Phone Call Analysis

Visualize phone call records with phone numbers as nodes:

```tsx
const callData = {
  nodes: [
    { 
      id: 'phone-1234567890', 
      label: '+1 (234) 567-8900', 
      type: 'phone',
      metadata: { primaryPhone: '1234567890' }
    },
    { 
      id: 'phone-0987654321', 
      label: '+1 (987) 654-3210', 
      type: 'phone',
      metadata: { primaryPhone: '0987654321' }
    },
  ],
  edges: [
    { 
      id: 'call1', 
      source: 'phone-1234567890', 
      target: 'phone-0987654321', 
      label: 'Called (5 times)',
      source_detail: '1234567890',
      target_detail: '0987654321'
    },
  ],
}
```

### 3. Organizational Charts

Show company hierarchy:

```tsx
const orgChart = {
  nodes: [
    { id: 'ceo', label: 'CEO', type: 'person' },
    { id: 'cto', label: 'CTO', type: 'person' },
    { id: 'cfo', label: 'CFO', type: 'person' },
    { id: 'eng', label: 'Engineering', type: 'organization' },
    { id: 'finance', label: 'Finance', type: 'organization' },
  ],
  edges: [
    { id: 'e1', source: 'ceo', target: 'cto', label: 'manages' },
    { id: 'e2', source: 'ceo', target: 'cfo', label: 'manages' },
    { id: 'e3', source: 'cto', target: 'eng', label: 'leads' },
    { id: 'e4', source: 'cfo', target: 'finance', label: 'leads' },
  ],
}

function OrgChart() {
  return (
    <GraphVisualization 
      data={orgChart}
      layout={{ name: 'breadthfirst', directed: true }}
    />
  )
}
```

### 4. Interactive Graph with Click Handlers

Respond to user interactions:

```tsx
import { useState } from 'react'
import { GraphVisualization, GraphData } from '@x025me/link-analysis'

function InteractiveGraph() {
  const [selectedNode, setSelectedNode] = useState<string | null>(null)
  const [graphData, setGraphData] = useState<GraphData>({
    nodes: [
      { id: '1', label: 'Node 1' },
      { id: '2', label: 'Node 2' },
    ],
    edges: [
      { id: 'e1', source: '1', target: '2' },
    ],
  })

  return (
    <div>
      {selectedNode && (
        <div>Selected: {selectedNode}</div>
      )}
      <div style={{ width: '100%', height: '600px' }}>
        <GraphVisualization
          data={graphData}
          onNodeClick={(nodeId) => {
            setSelectedNode(nodeId)
            console.log('Node clicked:', nodeId)
          }}
          onEdgeClick={(edgeId) => {
            console.log('Edge clicked:', edgeId)
          }}
        />
      </div>
    </div>
  )
}
```

## 🎨 Customizing the Layout

Choose from different layout algorithms:

### Force-Directed (Default)
Best for showing natural clusters and relationships:

```tsx
<GraphVisualization
  data={data}
  layout={{
    name: 'cose',
    animate: true,
    padding: 50,
    nodeSpacing: 100,
    edgeLength: 200,
  }}
/>
```

### Circular Layout
Organizes nodes in a circle:

```tsx
<GraphVisualization
  data={data}
  layout={{
    name: 'circle',
    animate: true,
    padding: 50,
  }}
/>
```

### Grid Layout
Arranges nodes in a grid:

```tsx
<GraphVisualization
  data={data}
  layout={{
    name: 'grid',
    animate: true,
    padding: 50,
  }}
/>
```

### Hierarchical Layout
Shows top-to-bottom hierarchy:

```tsx
<GraphVisualization
  data={data}
  layout={{
    name: 'breadthfirst',
    animate: true,
    padding: 50,
    directed: true,
  }}
/>
```

### Neo4j-Style Layout
Physics-heavy layout similar to Neo4j Bloom:

```tsx
<GraphVisualization
  data={data}
  layout={{
    name: 'cose-bilkent',
    animate: 'end',
    idealEdgeLength: 120,
    nodeRepulsion: 4500,
    gravity: 0.25,
  }}
/>
```

## 🔍 Filtering Nodes

### Include Specific Nodes

Show only certain nodes:

```tsx
const [includeFilters, setIncludeFilters] = useState<string[]>(['1234567890', '0987654321'])

<GraphVisualization
  data={data}
  includeFilters={includeFilters}
  onIncludeFiltersChange={setIncludeFilters}
/>
```

### Exclude Specific Nodes

Hide certain nodes:

```tsx
const [excludeFilters, setExcludeFilters] = useState<string[]>(['unwanted-node-id'])

<GraphVisualization
  data={data}
  excludeFilters={excludeFilters}
  onExcludeFiltersChange={setExcludeFilters}
/>
```

## 🎯 Using the Command Center

The component includes a built-in command center with two tabs:

- **Basic Tab**: Search, zoom, and navigation controls
- **Advanced Tab**: Filters, layout selection, and data management

Users can interact with the graph using:
- **Click**: Select a node
- **Ctrl/Cmd + Click**: Multi-select nodes
- **Drag**: Pan the graph
- **Scroll**: Zoom in/out
- **Hover**: See node/edge details in tooltips

## 📊 Data Format

### Basic Node Structure

```tsx
{
  id: 'unique-id',           // Required: Unique identifier
  label: 'Display Name',     // Required: Text shown on node
  type: 'person',            // Optional: Node type (affects color/shape)
  image: '/path/to/image',   // Optional: Node image
  metadata: {                // Optional: Additional data
    category: 'Research',
    description: 'A person',
    tags: ['important'],
    primaryPhone: '1234567890',
  },
  data: {                    // Optional: Raw data
    // Any additional fields
  },
}
```

### Basic Edge Structure

```tsx
{
  id: 'unique-edge-id',      // Required: Unique identifier
  source: 'node-id-1',       // Required: Source node ID
  target: 'node-id-2',       // Required: Target node ID
  label: 'connects to',      // Optional: Edge label
  weight: 5,                 // Optional: Edge weight
  source_detail: '1234567890', // Optional: Phone number or detail
  target_detail: '0987654321', // Optional: Phone number or detail
}
```

### Complete Example

```tsx
const completeData = {
  nodes: [
    {
      id: 'person-1',
      label: 'John Doe',
      type: 'person',
      image: '/images/john.jpg',
      metadata: {
        category: 'Employee',
        description: 'Software Engineer',
        tags: ['developer', 'senior'],
        primaryPhone: '+1234567890',
      },
    },
    {
      id: 'org-1',
      label: 'Tech Corp',
      type: 'organization',
      metadata: {
        category: 'Company',
        description: 'Technology company',
      },
    },
  ],
  edges: [
    {
      id: 'edge-1',
      source: 'person-1',
      target: 'org-1',
      label: 'works at',
      weight: 1,
    },
  ],
}
```

## 🌓 Theming

Wrap your app with `ThemeProvider` for dark/light theme support:

```tsx
import { ThemeProvider, GraphVisualization } from '@x025me/link-analysis'

function App() {
  return (
    <ThemeProvider>
      <GraphVisualization data={data} />
    </ThemeProvider>
  )
}
```

The component automatically adapts to light and dark themes.

## 🛠️ Working with Raw Data

If you have data in a different format, use the `normalizeToGraphData` utility:

```tsx
import { normalizeToGraphData } from '@x025me/link-analysis'

// Your raw data (can be in various formats)
const rawData = {
  people: [
    { id: 1, name: 'Alice', phone: '123-456-7890' },
    { id: 2, name: 'Bob', phone: '098-765-4321' },
  ],
  connections: [
    { from: 1, to: 2, type: 'knows' },
  ],
}

// Normalize it
const graphData = normalizeToGraphData(rawData)

// Use it
<GraphVisualization data={graphData} />
```

## 💡 Tips & Best Practices

### 1. Container Size
Always set a specific height for the container:

```tsx
// ✅ Good
<div style={{ width: '100%', height: '600px' }}>
  <GraphVisualization data={data} />
</div>

// ❌ Bad - no height specified
<div>
  <GraphVisualization data={data} />
</div>
```

### 2. Large Datasets
For graphs with many nodes (1000+), consider:
- Using simpler layouts (grid, circle)
- Filtering data before passing to component
- Using `includeFilters` to show only relevant nodes

### 3. Phone Number Formatting
The component automatically handles phone numbers. You can use:
- `+1234567890`
- `123-456-7890`
- `(123) 456-7890`
- `1234567890`

All formats are normalized automatically.

### 4. Node Types
Common node types with built-in styling:
- `person` - Blue circle
- `organization` - Green rounded rectangle
- `phone` - Hexagon shape
- `website` - Golden diamond
- `location` - Coral octagon

## ❓ Troubleshooting

### Graph not showing?

1. **Check container height**: Make sure the parent div has a height set
2. **Check data format**: Ensure `nodes` and `edges` arrays exist
3. **Check node IDs**: Source and target IDs in edges must match node IDs

### Styles not working?

1. **Import Ant Design styles**: Add `import 'antd/dist/reset.css'`
2. **Check CSS imports**: Ensure styles are imported before the component

### Nodes not connecting?

1. **Verify edge source/target**: Must exactly match node IDs
2. **Check for typos**: IDs are case-sensitive
3. **Use `normalizeToGraphData`**: If your data format is different

### Performance issues?

1. **Reduce node count**: Filter data before rendering
2. **Use simpler layouts**: Grid or circle for large datasets
3. **Disable animations**: Set `animate: false` in layout config

## 📚 API Reference

### GraphVisualization Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `data` | `GraphData` | **required** | Graph data with nodes and edges |
| `layout` | `GraphLayoutOptions` | `cose` | Layout configuration |
| `style` | `CSSProperties` | - | Custom container styles |
| `onNodeClick` | `(id: string) => void` | - | Called when node is clicked |
| `onEdgeClick` | `(id: string) => void` | - | Called when edge is clicked |
| `includeFilters` | `string[]` | `[]` | Show only these nodes |
| `excludeFilters` | `string[]` | `[]` | Hide these nodes |
| `onIncludeFiltersChange` | `(values: string[]) => void` | - | Update include filters |
| `onExcludeFiltersChange` | `(values: string[]) => void` | - | Update exclude filters |

### GraphData Type

```tsx
interface GraphData {
  nodes: Node[]
  edges: Edge[]
}
```

### Node Type

```tsx
interface Node {
  id: string
  label: string
  type?: string
  image?: string
  metadata?: NodeMetadata
  data?: Record<string, any>
}
```

### Edge Type

```tsx
interface Edge {
  id: string
  source: string
  target: string
  label?: string
  weight?: number
  source_detail?: string
  target_detail?: string
  data?: Record<string, any>
}
```

## 🎓 Examples

Check out these common patterns:

### Loading Data from API

```tsx
import { useState, useEffect } from 'react'
import { GraphVisualization, GraphData, normalizeToGraphData } from '@x025me/link-analysis'

function ApiGraph() {
  const [data, setData] = useState<GraphData>({ nodes: [], edges: [] })
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    fetch('/api/graph-data')
      .then(res => res.json())
      .then(raw => {
        const normalized = normalizeToGraphData(raw)
        setData(normalized)
        setLoading(false)
      })
  }, [])

  if (loading) return <div>Loading...</div>

  return (
    <div style={{ width: '100%', height: '600px' }}>
      <GraphVisualization data={data} />
    </div>
  )
}
```

### Dynamic Updates

```tsx
function DynamicGraph() {
  const [data, setData] = useState<GraphData>({
    nodes: [{ id: '1', label: 'Node 1' }],
    edges: [],
  })

  const addNode = () => {
    const newNode = {
      id: `node-${Date.now()}`,
      label: `Node ${data.nodes.length + 1}`,
    }
    setData(prev => ({
      ...prev,
      nodes: [...prev.nodes, newNode],
    }))
  }

  return (
    <div>
      <button onClick={addNode}>Add Node</button>
      <div style={{ width: '100%', height: '600px' }}>
        <GraphVisualization data={data} />
      </div>
    </div>
  )
}
```

## 📝 License

MIT

## 🤝 Support

Found a bug or have a question? [Open an issue](https://github.com/x025me/link-analysis/issues) on GitHub.

---

**Happy visualizing! 🎉**
