69 lines
2.9 KiB
TypeScript
69 lines
2.9 KiB
TypeScript
import React, { useState, useCallback } from 'react'
|
|
import { View, Text, ScrollView, StyleSheet, RefreshControl, ActivityIndicator } from 'react-native'
|
|
import { useFocusEffect } from 'expo-router'
|
|
import { COLORS } from '../../constants/Config'
|
|
import client from '../../services/api'
|
|
|
|
function ServiceNode({ name, deps, allNodes }: { name: string; deps: string[]; allNodes: Record<string, string[]> }) {
|
|
return (
|
|
<View style={n.wrap}>
|
|
<View style={n.node}><Text style={n.name}>{name}</Text></View>
|
|
{deps.length > 0 && (
|
|
<View style={n.depsWrap}>
|
|
{deps.map((d, i) => (
|
|
<View key={i} style={n.depRow}>
|
|
<View style={n.arrow} />
|
|
<View style={n.depBox}><Text style={n.depText}>{d}</Text></View>
|
|
</View>
|
|
))}
|
|
</View>
|
|
)}
|
|
</View>
|
|
)
|
|
}
|
|
const n = StyleSheet.create({
|
|
wrap: { marginBottom: 12 },
|
|
node: { backgroundColor: COLORS.accent, borderRadius: 8, paddingHorizontal: 14, paddingVertical: 8, alignSelf: 'flex-start' },
|
|
name: { color: '#fff', fontSize: 13, fontWeight: '700' },
|
|
depsWrap: { marginLeft: 20, marginTop: 4 },
|
|
depRow: { flexDirection: 'row', alignItems: 'center', marginTop: 4 },
|
|
arrow: { width: 20, height: 2, backgroundColor: COLORS.border, marginRight: 6 },
|
|
depBox: { backgroundColor: COLORS.light, borderRadius: 6, paddingHorizontal: 10, paddingVertical: 4 },
|
|
depText: { fontSize: 12, color: COLORS.text },
|
|
})
|
|
|
|
export default function DependencyMapScreen() {
|
|
const [data, setData] = useState<any>(null)
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
const load = useCallback(async () => {
|
|
setLoading(true)
|
|
try { const r = await client.get('/api/knowledge-graph/service-map'); setData(r.data) }
|
|
catch { setData(null) } finally { setLoading(false) }
|
|
}, [])
|
|
|
|
useFocusEffect(useCallback(() => { load() }, [load]))
|
|
|
|
if (loading) return <ActivityIndicator style={{ flex: 1 }} color={COLORS.accent} />
|
|
if (!data) return <Text style={s.empty}>서비스 의존성 맵을 불러올 수 없습니다.</Text>
|
|
|
|
const nodes: Record<string, string[]> = data.dependencies ?? data.services ?? {}
|
|
|
|
return (
|
|
<ScrollView style={s.container} refreshControl={<RefreshControl refreshing={loading} onRefresh={load} />} contentContainerStyle={{ padding: 16 }}>
|
|
<Text style={s.title}>서비스 의존성 맵</Text>
|
|
<Text style={s.sub}>노드: {Object.keys(nodes).length}개 · 관계: {Object.values(nodes).flat().length}개</Text>
|
|
{Object.entries(nodes).map(([name, deps]) => (
|
|
<ServiceNode key={name} name={name} deps={deps as string[]} allNodes={nodes} />
|
|
))}
|
|
</ScrollView>
|
|
)
|
|
}
|
|
|
|
const s = StyleSheet.create({
|
|
container: { flex: 1, backgroundColor: COLORS.bg },
|
|
empty: { textAlign: 'center', color: COLORS.muted, marginTop: 40 },
|
|
title: { fontSize: 20, fontWeight: '800', color: COLORS.text, marginBottom: 4 },
|
|
sub: { fontSize: 12, color: COLORS.muted, marginBottom: 16 },
|
|
})
|