import '../analysis/graph-viewer.js' import gql from 'graphql-tag' import { css, html, nothing } from 'lit' import { customElement, query, state } from 'lit/decorators.js' import { client } from '@operato/graphql' import { i18next, localize } from '@operato/i18n' import { PageView } from '@operato/shell' import { ScrollbarStyles } from '@operato/styles' import { GraphData, Node, Relationship } from '../analysis/graph-data.js' import { GraphViewer } from '../analysis/graph-viewer.js' import { GraphViewerStyles } from '../analysis/graph-viewer-style' @customElement('integration-analysis') export class IntegrationAnalysis extends localize(i18next)(PageView) { static styles = [ GraphViewerStyles, ScrollbarStyles, css` :host { position: relative; display: flex; flex-direction: column; overflow: hidden; background-color: var(--md-sys-color-background); padding: 0; } [graph-container] { flex: 1; margin: 0; padding: 0; } .graph-info { position: absolute; top: 10px; left: 10px; opacity: 0.8; } ` ] @state() private graphViewer?: GraphViewer @state() private analysis?: { nodes: Node[]; relationships: Relationship[] } @state() private info?: { cls: string; property: string; value: string } | null @query('[graph-container]') container!: HTMLDivElement @query('[node-info]') nodeInfo!: HTMLAnchorElement get context() { return { title: i18next.t('text.integration analysis'), search: { handler: search => this.onSearch(search) }, help: 'integration/ui/integration-analysis' } } render() { const info = this.info return html`
console.log(e.detail)} @node-mouseleave=${(e: CustomEvent) => console.log(e.detail)} >
${info ? html`${info.property}${info.value}` : nothing}
` } updated(changes) { if (changes.has('analysis')) { if (!this.graphViewer) { this.graphViewer = new GraphViewer(this.container, { highlight: [ { class: 'Connection', property: 'missing', value: true }, { class: 'Scenario', property: 'missing', value: true }, { class: 'Tag', property: 'missing', value: true } ], minCollision: 60, graphData: { results: [ { columns: [], data: [ { graph: this.analysis } ] } ], errors: [] }, nodeRadius: 25, onNodeDoubleClick: node => { switch (node.id) { case '25': // Google window.open(node.properties.url, '_blank') break default: break } }, zoomFit: true }) } else { this.graphViewer.updateWithGraphData({ results: [{ columns: [], data: [{ graph: this.analysis! }] }], errors: [] }) } } if (changes.has('info') && this.info) { } } async pageUpdated(changes) { if ('active' in changes) { if (this.active) { await this.fetchIntegrationAnalysis() this.graphViewer?.simulation.restart() } else { this.graphViewer?.simulation.stop() } } } async fetchIntegrationAnalysis() { const response = await client.query({ query: gql` query { integrationAnalysis } ` }) this.analysis = response.data.integrationAnalysis } async onSearch(search: string) { if (!this.graphViewer) { return } if (!search) { this.graphViewer.updateWithGraphData({ results: [{ columns: [], data: [{ graph: this.analysis! }] }], errors: [] }) return } const { nodes = [], relationships = [] } = this.analysis || {} // 검색어와 일치하는 노드를 필터링 const matchingNodes = nodes.filter(node => { return node.properties.name && node.properties.name.toLowerCase().includes(search.toLowerCase()) }) if (matchingNodes.length === 0) { this.graphViewer.updateWithGraphData({ results: [{ columns: [], data: [{ graph: { nodes: [], relationships: [] } }] }], errors: [] }) return } // 1차로 연결된 노드와 관계를 찾기 위해 관련된 노드 ID를 추적 const relatedNodeIds = new Set() const filteredNodes = [] as Node[] const filteredRelationships = [] as Relationship[] matchingNodes.forEach(node => { relatedNodeIds.add(node.id) filteredNodes.push(node) relationships.forEach(relationship => { const { source, target } = relationship || {} if (source!.id === node.id || target!.id === node.id) { relatedNodeIds.add(source!.id) relatedNodeIds.add(target!.id) filteredRelationships.push(relationship!) } }) }) // 관련된 노드 추가 relatedNodeIds.forEach(nodeId => { const node = nodes.find(n => n.id === nodeId) if (node) { filteredNodes.push(node) } }) // 새로운 GraphData 구성 const filteredGraphData: GraphData = { results: [ { columns: [], data: [ { graph: { nodes: filteredNodes, relationships: filteredRelationships } } ] } ], errors: [] } // GraphViewer 업데이트 this.graphViewer.updateWithGraphData(filteredGraphData) } }