feat: flow chart

This commit is contained in:
scarqin
2024-12-24 15:31:02 +08:00
parent 2a1581acdb
commit 604200e1db
6 changed files with 30 additions and 52 deletions
@@ -39,15 +39,18 @@ interface ApiResponse {
const calculateNodePositions = (models: ModelData[], startY = LAYOUT.NODE_START_Y, gap = LAYOUT.NODE_GAP) => {
return models.reduce(
(acc, model, index) => {
acc[model.id] = {
x: LAYOUT.MODEL_NODE_X,
y: startY + index * gap
const y = startY + index * gap
return {
...acc,
[model.id]: {
x: LAYOUT.MODEL_NODE_X,
y
},
[`${model.id}-keys`]: {
x: LAYOUT.KEY_NODE_X,
y
}
}
acc[`${model.id}-keys`] = {
x: LAYOUT.KEY_NODE_X,
y: startY + index * gap
}
return acc
},
{} as Record<string, { x: number; y: number }>
)
@@ -83,17 +86,24 @@ const AIFlowChart = () => {
useEffect(() => {
if (!modelData.length) return
const positions = calculateNodePositions(modelData)
// subtract 5 to make sure the service node is above the top model node
const serviceY = positions[modelData[0].id].y - 5
const newNodes = [
{
id: 'apiService',
type: 'serviceCard',
position: { x: LAYOUT.SERVICE_NODE_X, y: LAYOUT.NODE_START_Y },
data: {}
position: { x: LAYOUT.SERVICE_NODE_X, y: serviceY },
data: {
title: 'API Service',
count: modelData.length
}
},
...modelData.map((model) => ({
id: model.id,
type: 'modelCard',
position: calculateNodePositions(modelData)[model.id],
position: positions[model.id],
data: {
title: model.name,
status: model.status,
@@ -104,7 +114,7 @@ const AIFlowChart = () => {
...modelData.map((model) => ({
id: `${model.id}-keys`,
type: 'keyCard',
position: calculateNodePositions(modelData)[`${model.id}-keys`],
position: positions[`${model.id}-keys`],
data: {
title: 'API Keys',
keys: model.keys.map((key, index) => ({
@@ -121,14 +131,15 @@ const AIFlowChart = () => {
id: `service-${model.id}`,
source: 'apiService',
target: model.id,
label: `apis(${model.api_count})`,
markerEnd: { type: 'arrow' },
data: { id: model.id }
label: `${model.api_count} apis`,
data: { id: model.id },
animated: true
})),
...modelData.map((model) => ({
id: `${model.id}-keys-edge`,
source: model.id,
target: `${model.id}-keys`,
label: `${model.key_count} keys`,
animated: true
}))
]
@@ -35,7 +35,7 @@ export default function CustomEdge({
target="_blank"
style={{
position: 'absolute',
transform: `translate(${targetX - 80}px,${targetY}px)`,
transform: `translate(${targetX - 80}px,${targetY - 20}px)`,
borderRadius: '4px',
fontSize: 12,
fontWeight: 500,
@@ -20,7 +20,7 @@ export const ModelCardNode: React.FC<{ data: ModelCardNodeData }> = ({ data }) =
const { title, status, defaultModel, logo } = data
return (
<div
className="node-card bg-white rounded-lg shadow-sm p-4 min-w-[280px] relative group"
className="node-card bg-white rounded-lg shadow-sm p-4 min-w-[280px] group"
style={{ border: '1px solid var(--border-color)' }}
>
<Handle type="target" position={Position.Left} />
@@ -5,7 +5,7 @@ import React from 'react'
export const ServiceCardNode: React.FC<NodeProps> = () => {
return (
<div
className="node-card bg-white rounded-lg shadow-sm p-4 min-w-[150px] relative nodrag"
className="node-card bg-white rounded-lg shadow-sm p-4 min-w-[150px] nodrag"
style={{ border: '1px solid var(--border-color)' }}
>
<Handle type="source" position={Position.Right} />
@@ -3,5 +3,5 @@ export const LAYOUT = {
NODE_START_Y: 50,
NODE_GAP: 120,
MODEL_NODE_X: 500,
KEY_NODE_X: 850,
KEY_NODE_X: 900,
} as const;
@@ -6,39 +6,6 @@
width: auto;
max-width: 100%;
}
.react-flow__edge-path {
stroke: var(--primary-color);
stroke-width: 2;
}
.react-flow__edge-text {
font-size: 12px;
fill: #666;
}
.react-flow__controls {
box-shadow: 0 0 2px rgba(0, 0, 0, 0.1);
border: 1px solid #ededed;
}
.react-flow__controls-button {
border: none;
background: white;
border-bottom: 1px solid #ededed;
box-sizing: content-box;
display: flex;
justify-content: center;
align-items: center;
width: 16px;
height: 16px;
cursor: pointer;
user-select: none;
padding: 5px;
}
.react-flow__controls-button:hover {
background: #f8f8f8;
}
/* Custom Node Styles */
.custom-node {