diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3e56d752..fdab7ac4 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1435,7 +1435,6 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", - "peer": true, "engines": { "node": ">=12" } @@ -1913,7 +1912,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -2053,7 +2051,6 @@ "integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -2128,7 +2125,6 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.25.tgz", "integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==", "license": "MIT", - "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.25", "@vue/compiler-sfc": "3.5.25", diff --git a/frontend/src/components/Step1GraphBuild.vue b/frontend/src/components/Step1GraphBuild.vue index 687d1c7b..990f3e05 100644 --- a/frontend/src/components/Step1GraphBuild.vue +++ b/frontend/src/components/Step1GraphBuild.vue @@ -10,6 +10,7 @@
{{ $t('step1.ontologyCompleted') }} + FAILED {{ $t('step1.ontologyGenerating') }} {{ $t('step1.ontologyPending') }}
@@ -22,10 +23,16 @@

-
+
{{ ontologyProgress.message || $t('step1.analyzingDocs') }}
+ + +
+ ⚠️ + {{ errorMsg }} +
@@ -114,6 +121,7 @@
{{ $t('step1.ontologyCompleted') }} + FAILED {{ buildProgress?.progress || 0 }}% {{ $t('step1.ontologyPending') }}
@@ -125,6 +133,12 @@ {{ $t('step1.graphRagDesc') }}

+ +
+ ⚠️ + {{ errorMsg }} +
+
@@ -201,7 +215,8 @@ const props = defineProps({ ontologyProgress: Object, buildProgress: Object, graphData: Object, - systemLogs: { type: Array, default: () => [] } + systemLogs: { type: Array, default: () => [] }, + errorMsg: { type: String, default: '' } }) defineEmits(['next-step']) @@ -349,6 +364,7 @@ watch(() => props.systemLogs.length, () => { .badge.processing { background: #FF5722; color: #FFF; } .badge.accent { background: #FF5722; color: #FFF; } .badge.pending { background: #F5F5F5; color: #999; } +.badge.error { background: #FFEBEE; color: #D32F2F; } .api-note { font-family: 'JetBrains Mono', monospace; @@ -364,6 +380,28 @@ watch(() => props.systemLogs.length, () => { margin-bottom: 16px; } +.error-alert { + display: flex; + align-items: flex-start; + gap: 8px; + background: #FFF5F5; + border: 1px solid #FFCDD2; + border-radius: 6px; + padding: 12px; + margin-top: 8px; +} + +.error-icon { + font-size: 14px; +} + +.error-text { + font-size: 12px; + color: #C62828; + line-height: 1.4; + word-break: break-word; +} + /* Step 01 Tags */ .tags-container { margin-top: 12px; diff --git a/frontend/src/views/MainView.vue b/frontend/src/views/MainView.vue index 513c70d8..e8c48bd6 100644 --- a/frontend/src/views/MainView.vue +++ b/frontend/src/views/MainView.vue @@ -59,6 +59,7 @@ :buildProgress="buildProgress" :graphData="graphData" :systemLogs="systemLogs" + :errorMsg="error" @next-step="handleNextStep" /> @@ -83,7 +84,7 @@ import { useI18n } from 'vue-i18n' import GraphPanel from '../components/GraphPanel.vue' import Step1GraphBuild from '../components/Step1GraphBuild.vue' import Step2EnvSetup from '../components/Step2EnvSetup.vue' -import { generateOntology, getProject, buildGraph, getTaskStatus, getGraphData } from '../api/graph' +import { generateOntology, getProject, buildGraph, getTaskStatus, getGraphData, getGraphConfig } from '../api/graph' import { getPendingUpload, clearPendingUpload } from '../store/pendingUpload' import LanguageSwitcher from '../components/LanguageSwitcher.vue' @@ -114,6 +115,9 @@ const systemLogs = ref([]) let pollTimer = null let graphPollTimer = null +// Graph polling config (fetched from backend) +const graphPollInterval = ref(0) // 0 = manual only + // --- Computed Layout Styles --- const leftPanelStyle = computed(() => { if (viewMode.value === 'graph') return { width: '100%', opacity: 1, transform: 'translateX(0)' } @@ -184,6 +188,19 @@ const handleGoBack = () => { const initProject = async () => { addLog('Project view initialized.') + + // Fetch graph polling config from backend + try { + const configRes = await getGraphConfig() + if (configRes.success && configRes.data) { + graphPollInterval.value = configRes.data.poll_interval || 0 + addLog(`Graph config loaded: poll_interval=${graphPollInterval.value}s, cache_ttl=${configRes.data.cache_ttl}s`) + } + } catch (err) { + addLog('Could not load graph config, defaulting to manual refresh only.') + graphPollInterval.value = 0 + } + if (currentProjectId.value === 'new') { await handleNewProject() } else { @@ -295,9 +312,17 @@ const startBuildGraph = async () => { } const startGraphPolling = () => { - addLog('Started polling for graph data...') + // Always do one immediate fetch fetchGraphData() - graphPollTimer = setInterval(fetchGraphData, 10000) + + // Only set up automatic polling if poll_interval > 0 (paid plan) + if (graphPollInterval.value > 0) { + const intervalMs = graphPollInterval.value * 1000 + addLog(`Started automatic graph polling (every ${graphPollInterval.value}s)...`) + graphPollTimer = setInterval(fetchGraphData, intervalMs) + } else { + addLog('Automatic graph polling disabled (FREE plan). Use manual refresh.') + } } const fetchGraphData = async () => { diff --git a/frontend/src/views/Process.vue b/frontend/src/views/Process.vue index 2d2d3cc1..591c5473 100644 --- a/frontend/src/views/Process.vue +++ b/frontend/src/views/Process.vue @@ -414,7 +414,7 @@