This is an automated email from the ASF dual-hosted git repository.

wusheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-banyandb.git


The following commit(s) were added to refs/heads/main by this push:
     new e9811f0c refactor(UI): Optimize router for better usability (#834)
e9811f0c is described below

commit e9811f0cfbca8dad30eddddf467c6bac54068978
Author: Fine0830 <[email protected]>
AuthorDate: Tue Nov 4 16:51:07 2025 +0800

    refactor(UI): Optimize router for better usability (#834)
---
 CHANGES.md                            |   1 +
 ui/src/components/GroupTree/index.vue |   2 -
 ui/src/router/constants.js            |  47 ++++++
 ui/src/router/index.js                | 283 ++++------------------------------
 ui/src/router/modules/dashboard.js    |  27 ++++
 ui/src/router/modules/measure.js      |  58 +++++++
 ui/src/router/modules/property.js     |  50 ++++++
 ui/src/router/modules/query.js        |  27 ++++
 ui/src/router/modules/stream.js       |  52 +++++++
 ui/src/router/modules/trace.js        |  52 +++++++
 ui/src/router/routeFactory.js         | 113 ++++++++++++++
 ui/src/views/Measure/index.vue        |   3 +-
 ui/src/views/Property/index.vue       |   3 +-
 ui/src/views/Stream/index.vue         |   3 +-
 ui/src/views/Trace/index.vue          |   3 +-
 ui/src/views/constants.js             |  21 +++
 16 files changed, 489 insertions(+), 256 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 0b0e8b89..12224018 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -53,6 +53,7 @@ Release Notes.
 - Implement Trace Tree for debug mode.
 - Implement bydbQL.
 - UI: Implement the Query Page for BydbQL.
+- Refactor router for better usability.
 - Implement the handoff queue for Trace.
 
 ### Bug Fixes
diff --git a/ui/src/components/GroupTree/index.vue 
b/ui/src/components/GroupTree/index.vue
index f1bdb9ba..54dcc261 100644
--- a/ui/src/components/GroupTree/index.vue
+++ b/ui/src/components/GroupTree/index.vue
@@ -361,7 +361,6 @@
         operator: 'create',
         group: currentNode.value.group,
         type,
-        schema: props.type,
       },
     };
     router.push(route);
@@ -382,7 +381,6 @@
         group: currentNode.value.group,
         name: currentNode.value.name,
         type,
-        schema: props.type,
       },
     };
     router.push(route);
diff --git a/ui/src/router/constants.js b/ui/src/router/constants.js
new file mode 100644
index 00000000..d4433cd4
--- /dev/null
+++ b/ui/src/router/constants.js
@@ -0,0 +1,47 @@
+/*
+ * Licensed to Apache Software Foundation (ASF) under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Apache Software Foundation (ASF) licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/** Base path for all BanyanDB routes */
+export const BASE_PATH = '/banyandb';
+
+/** Route parameter patterns for CRUD operations */
+export const ROUTE_PARAMS = {
+  OPERATOR_READ: ':type/:operator/:group/:name',
+  OPERATOR_CREATE: ':type/:operator/:group',
+  OPERATOR_EDIT: ':type/:operator/:group/:name',
+};
+
+/** Schema type identifiers */
+export const SCHEMA_TYPES = {
+  STREAM: 'stream',
+  MEASURE: 'measure',
+  PROPERTY: 'property',
+  TRACE: 'trace',
+};
+
+/** Component paths for dynamic imports (reference only) */
+export const COMPONENTS = {
+  START: '@/components/Start/index.vue',
+  INDEX_RULE: '@/components/IndexRule/index.vue',
+  INDEX_RULE_EDITOR: '@/components/IndexRule/Editor.vue',
+  INDEX_RULE_BINDING: '@/components/IndexRuleBinding/index.vue',
+  INDEX_RULE_BINDING_EDITOR: '@/components/IndexRuleBinding/Editor.vue',
+  TOPN_AGG: '@/components/TopNAggregation/index.vue',
+  TOPN_AGG_EDITOR: '@/components/TopNAggregation/Editor.vue',
+};
diff --git a/ui/src/router/index.js b/ui/src/router/index.js
index 2cf7c368..08542e4e 100644
--- a/ui/src/router/index.js
+++ b/ui/src/router/index.js
@@ -20,272 +20,55 @@
 import { createRouter, createWebHistory } from 'vue-router';
 import Header from '@/components/Header/index.vue';
 import { MENU_DEFAULT_PATH } from '@/components/Header/components/constants';
+import { BASE_PATH } from './constants';
 
+// Import route modules
+import dashboardRoute from './modules/dashboard';
+import streamRoutes from './modules/stream';
+import measureRoutes from './modules/measure';
+import propertyRoutes from './modules/property';
+import traceRoutes from './modules/trace';
+import queryRoute from './modules/query';
+
+/**
+ * Router configuration for BanyanDB UI
+ *
+ * Route structure:
+ * - / : Redirects to /banyandb
+ * - /banyandb : Main application layout with header
+ *   - /dashboard : Dashboard view
+ *   - /stream : Stream schema management
+ *   - /measure : Measure schema management
+ *   - /property : Property management
+ *   - /trace : Trace schema management
+ *   - /query : BydbQL query interface
+ * - /* : 404 error page
+ * Structure: /banyandb/{module}/{operation}
+ * Modules: dashboard, stream, measure, property, trace, query
+ */
 const router = createRouter({
   history: createWebHistory(import.meta.env.BASE_URL),
   routes: [
+    // Root redirect
     {
       path: '/',
-      redirect: '/banyandb',
+      redirect: BASE_PATH,
     },
+    // Main application routes
     {
-      path: '/banyandb',
+      path: BASE_PATH,
       component: Header,
       name: 'banyandb',
       redirect: MENU_DEFAULT_PATH,
-      meta: {
-        keepAlive: false,
-      },
-      children: [
-        {
-          path: '/banyandb/dashboard',
-          name: 'dashboard',
-          component: () => import('@/views/Dashboard/index.vue'),
-        },
-        {
-          path: '/banyandb/stream',
-          name: 'streamHome',
-          redirect: '/banyandb/stream/start',
-          component: () => import('@/views/Stream/index.vue'),
-          children: [
-            {
-              path: '/banyandb/stream/start',
-              name: 'streamStart',
-              component: () => import('@/components/Start/index.vue'),
-              meta: {
-                type: 'stream',
-              },
-            },
-            {
-              path: 
'/banyandb/stream/operator-read/:type/:operator/:group/:name',
-              name: 'stream',
-              component: () => import('@/views/Stream/stream.vue'),
-            },
-            {
-              path: '/banyandb/stream/operator-create/:type/:operator/:group',
-              name: 'create-stream',
-              component: () => import('@/views/Stream/createEdit.vue'),
-            },
-            {
-              path: 
'/banyandb/stream/operator-edit/:type/:operator/:group/:name',
-              name: 'edit-stream',
-              component: () => import('@/views/Stream/createEdit.vue'),
-            },
-            {
-              path: 
'/banyandb/stream/index-rule/operator-read/:type/:operator/:group/:name',
-              name: 'stream-index-rule',
-              component: () => import('@/components/IndexRule/index.vue'),
-            },
-            {
-              path: 
'/banyandb/:schema/index-rule/operator-create/:type/:operator/:group',
-              name: 'stream-create-index-rule',
-              component: () => import('@/components/IndexRule/Editor.vue'),
-            },
-            {
-              path: 
'/banyandb/:schema/index-rule/operator-edit/:type/:operator/:group/:name',
-              name: 'stream-edit-index-rule',
-              component: () => import('@/components/IndexRule/Editor.vue'),
-            },
-            {
-              path: 
'/banyandb/stream/index-rule-binding/operator-read/:type/:operator/:group/:name',
-              name: 'stream-index-rule-binding',
-              component: () => 
import('@/components/IndexRuleBinding/index.vue'),
-            },
-            {
-              path: 
'/banyandb/:schema/index-rule-binding/operator-create/:type/:operator/:group',
-              name: 'stream-create-index-rule-binding',
-              component: () => 
import('@/components/IndexRuleBinding/Editor.vue'),
-            },
-            {
-              path: 
'/banyandb/:schema/index-rule-binding/operator-edit/:type/:operator/:group/:name',
-              name: 'stream-edit-index-rule-binding',
-              component: () => 
import('@/components/IndexRuleBinding/Editor.vue'),
-            },
-          ],
-        },
-        {
-          path: '/banyandb/measure',
-          name: 'measureHome',
-          redirect: '/banyandb/measure/start',
-          component: () => import('@/views/Measure/index.vue'),
-          children: [
-            {
-              path: '/banyandb/measure/start',
-              name: 'measureStart',
-              component: () => import('@/components/Start/index.vue'),
-              meta: {
-                type: 'measure',
-              },
-            },
-            {
-              path: 
'/banyandb/measure/operator-read/:type/:operator/:group/:name',
-              name: 'measure',
-              component: () => import('@/views/Measure/measure.vue'),
-            },
-            {
-              path: '/banyandb/measure/operator-create/:type/:operator/:group',
-              name: 'create-measure',
-              component: () => import('@/views/Measure/createEdit.vue'),
-            },
-            {
-              path: 
'/banyandb/measure/operator-edit/:type/:operator/:group/:name',
-              name: 'edit-measure',
-              component: () => import('@/views/Stream/createEdit.vue'),
-            },
-            {
-              path: 
'/banyandb/measure/index-rule/operator-read/:type/:operator/:group/:name',
-              name: 'measure-index-rule',
-              component: () => import('@/components/IndexRule/index.vue'),
-            },
-            {
-              path: 
'/banyandb/:schema/index-rule/operator-create/:type/:operator/:group',
-              name: 'measure-create-index-rule',
-              component: () => import('@/components/IndexRule/Editor.vue'),
-            },
-            {
-              path: 
'/banyandb/:schema/index-rule/operator-edit/:type/:operator/:group/:name',
-              name: 'measure-edit-index-rule',
-              component: () => import('@/components/IndexRule/Editor.vue'),
-            },
-            {
-              path: 
'/banyandb/measure/index-rule-binding/operator-read/:type/:operator/:group/:name',
-              name: 'measure-index-rule-binding',
-              component: () => 
import('@/components/IndexRuleBinding/index.vue'),
-            },
-            {
-              path: 
'/banyandb/:schema/index-rule-binding/operator-create/:type/:operator/:group',
-              name: 'measure-create-index-rule-binding',
-              component: () => 
import('@/components/IndexRuleBinding/Editor.vue'),
-            },
-            {
-              path: 
'/banyandb/:schema/index-rule-binding/operator-edit/:type/:operator/:group/:name',
-              name: 'measure-edit-index-rule-binding',
-              component: () => 
import('@/components/IndexRuleBinding/Editor.vue'),
-            },
-            {
-              path: 
'/banyandb/measure/topn-agg/operator-read/:type/:operator/:group/:name',
-              name: 'measure-topn-agg',
-              component: () => 
import('@/components/TopNAggregation/index.vue'),
-            },
-            {
-              path: 
'/banyandb/:schema/topn-agg/operator-create/:type/:operator/:group',
-              name: 'measure-create-topn-agg',
-              component: () => 
import('@/components/TopNAggregation/Editor.vue'),
-            },
-            {
-              path: 
'/banyandb/:schema/topn-agg/operator-edit/:type/:operator/:group/:name',
-              name: 'measure-edit-topn-agg',
-              component: () => 
import('@/components/TopNAggregation/Editor.vue'),
-            },
-          ],
-        },
-        {
-          path: '/banyandb/property',
-          name: 'Property',
-          redirect: '/banyandb/property/start',
-          component: () => import('@/views/Property/index.vue'),
-          children: [
-            {
-              path: '/banyandb/property/start',
-              name: 'propertyStart',
-              component: () => import('@/components/Start/index.vue'),
-              meta: {
-                type: 'property',
-              },
-            },
-            {
-              path: 
'/banyandb/property/operator-read/:type/:operator/:group/:name',
-              name: 'property',
-              component: () => 
import('@/components/Property/PropertyRead.vue'),
-            },
-            {
-              path: 
'/banyandb/property/operator-edit/:type/:operator/:group/:name',
-              name: 'edit-property',
-              component: () => import('@/views/Property/createEdit.vue'),
-            },
-            {
-              path: 
'/banyandb/property/operator-create/:type/:operator/:group',
-              name: 'create-property',
-              component: () => import('@/views/Property/createEdit.vue'),
-            },
-          ],
-        },
-        {
-          path: '/banyandb/trace',
-          name: 'traceHome',
-          redirect: '/banyandb/trace/start',
-          component: () => import('@/views/Trace/index.vue'),
-          children: [
-            {
-              path: '/banyandb/trace/start',
-              name: 'traceStart',
-              component: () => import('@/components/Start/index.vue'),
-              meta: {
-                type: 'trace',
-              },
-            },
-            {
-              path: 
'/banyandb/trace/operator-read/:type/:operator/:group/:name',
-              name: 'trace',
-              component: () => import('@/components/Trace/TraceRead.vue'),
-            },
-            {
-              path: 
'/banyandb/trace/operator-edit/:type/:operator/:group/:name',
-              name: 'edit-trace',
-              component: () => import('@/views/Trace/createEdit.vue'),
-            },
-            {
-              path: '/banyandb/trace/operator-create/:type/:operator/:group',
-              name: 'create-trace',
-              component: () => import('@/views/Trace/createEdit.vue'),
-            },
-            {
-              path: 
'/banyandb/trace/index-rule/operator-read/:type/:operator/:group/:name',
-              name: 'trace-index-rule',
-              component: () => import('@/components/IndexRule/index.vue'),
-            },
-            {
-              path: 
'/banyandb/:schema/index-rule/operator-create/:type/:operator/:group',
-              name: 'trace-create-index-rule',
-              component: () => import('@/components/IndexRule/Editor.vue'),
-            },
-            {
-              path: 
'/banyandb/:schema/index-rule/operator-edit/:type/:operator/:group/:name',
-              name: 'trace-edit-index-rule',
-              component: () => import('@/components/IndexRule/Editor.vue'),
-            },
-            {
-              path: 
'/banyandb/trace/index-rule-binding/operator-read/:type/:operator/:group/:name',
-              name: 'trace-index-rule-binding',
-              component: () => 
import('@/components/IndexRuleBinding/index.vue'),
-            },
-            {
-              path: 
'/banyandb/:schema/index-rule-binding/operator-create/:type/:operator/:group',
-              name: 'trace-create-index-rule-binding',
-              component: () => 
import('@/components/IndexRuleBinding/Editor.vue'),
-            },
-            {
-              path: 
'/banyandb/:schema/index-rule-binding/operator-edit/:type/:operator/:group/:name',
-              name: 'trace-edit-index-rule-binding',
-              component: () => 
import('@/components/IndexRuleBinding/Editor.vue'),
-            },
-          ],
-        },
-        {
-          path: '/banyandb/query',
-          name: 'query',
-          component: () => import('@/views/Query/BydbQL.vue'),
-        },
-      ],
+      meta: { keepAlive: false },
+      children: [dashboardRoute, streamRoutes, measureRoutes, propertyRoutes, 
traceRoutes, queryRoute],
     },
+    // 404 Not Found
     {
-      // will match everything
       path: '/:pathMatch(.*)',
       name: 'NotFound',
       component: Header,
-      meta: {
-        keepAlive: false,
-      },
+      meta: { keepAlive: false },
       children: [
         {
           path: '/:pathMatch(.*)',
diff --git a/ui/src/router/modules/dashboard.js 
b/ui/src/router/modules/dashboard.js
new file mode 100644
index 00000000..19748828
--- /dev/null
+++ b/ui/src/router/modules/dashboard.js
@@ -0,0 +1,27 @@
+/*
+ * Licensed to Apache Software Foundation (ASF) under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Apache Software Foundation (ASF) licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { BASE_PATH } from '../constants';
+
+/** Dashboard route */
+export default {
+  path: `${BASE_PATH}/dashboard`,
+  name: 'dashboard',
+  component: () => import('@/views/Dashboard/index.vue'),
+};
diff --git a/ui/src/router/modules/measure.js b/ui/src/router/modules/measure.js
new file mode 100644
index 00000000..46aa7e39
--- /dev/null
+++ b/ui/src/router/modules/measure.js
@@ -0,0 +1,58 @@
+/*
+ * Licensed to Apache Software Foundation (ASF) under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Apache Software Foundation (ASF) licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { BASE_PATH, SCHEMA_TYPES, ROUTE_PARAMS } from '../constants';
+import {
+  createStartRoute,
+  createIndexRuleRoutes,
+  createIndexRuleBindingRoutes,
+  createTopNAggRoutes,
+} from '../routeFactory';
+
+/**
+ * Measure schema routes
+ * Includes: CRUD, index rules, index rule bindings, TopN aggregations
+ */
+export default {
+  path: `${BASE_PATH}/measure`,
+  name: 'measureHome',
+  redirect: `${BASE_PATH}/measure/start`,
+  component: () => import('@/views/Measure/index.vue'),
+  children: [
+    createStartRoute(SCHEMA_TYPES.MEASURE),
+    {
+      path: ROUTE_PARAMS.OPERATOR_READ,
+      name: SCHEMA_TYPES.MEASURE,
+      component: () => import('@/views/Measure/measure.vue'),
+    },
+    {
+      path: ROUTE_PARAMS.OPERATOR_CREATE,
+      name: `create-${SCHEMA_TYPES.MEASURE}`,
+      component: () => import('@/views/Measure/createEdit.vue'),
+    },
+    {
+      path: ROUTE_PARAMS.OPERATOR_EDIT,
+      name: `edit-${SCHEMA_TYPES.MEASURE}`,
+      component: () => import('@/views/Measure/createEdit.vue'),
+    },
+    ...createIndexRuleRoutes(SCHEMA_TYPES.MEASURE),
+    ...createIndexRuleBindingRoutes(SCHEMA_TYPES.MEASURE),
+    ...createTopNAggRoutes(SCHEMA_TYPES.MEASURE),
+  ],
+};
diff --git a/ui/src/router/modules/property.js 
b/ui/src/router/modules/property.js
new file mode 100644
index 00000000..335b9bfc
--- /dev/null
+++ b/ui/src/router/modules/property.js
@@ -0,0 +1,50 @@
+/*
+ * Licensed to Apache Software Foundation (ASF) under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Apache Software Foundation (ASF) licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { BASE_PATH, SCHEMA_TYPES, ROUTE_PARAMS } from '../constants';
+import { createStartRoute } from '../routeFactory';
+
+/**
+ * Property routes
+ * Includes: CRUD only
+ */
+export default {
+  path: `${BASE_PATH}/property`,
+  name: 'Property',
+  redirect: `${BASE_PATH}/property/start`,
+  component: () => import('@/views/Property/index.vue'),
+  children: [
+    createStartRoute(SCHEMA_TYPES.PROPERTY),
+    {
+      path: ROUTE_PARAMS.OPERATOR_READ,
+      name: SCHEMA_TYPES.PROPERTY,
+      component: () => import('@/components/Property/PropertyRead.vue'),
+    },
+    {
+      path: ROUTE_PARAMS.OPERATOR_CREATE,
+      name: `create-${SCHEMA_TYPES.PROPERTY}`,
+      component: () => import('@/views/Property/createEdit.vue'),
+    },
+    {
+      path: ROUTE_PARAMS.OPERATOR_EDIT,
+      name: `edit-${SCHEMA_TYPES.PROPERTY}`,
+      component: () => import('@/views/Property/createEdit.vue'),
+    },
+  ],
+};
diff --git a/ui/src/router/modules/query.js b/ui/src/router/modules/query.js
new file mode 100644
index 00000000..11bcf646
--- /dev/null
+++ b/ui/src/router/modules/query.js
@@ -0,0 +1,27 @@
+/*
+ * Licensed to Apache Software Foundation (ASF) under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Apache Software Foundation (ASF) licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { BASE_PATH } from '../constants';
+
+/** BydbQL query interface route */
+export default {
+  path: `${BASE_PATH}/query`,
+  name: 'BydbQL',
+  component: () => import('@/views/Query/BydbQL.vue'),
+};
diff --git a/ui/src/router/modules/stream.js b/ui/src/router/modules/stream.js
new file mode 100644
index 00000000..3cf12525
--- /dev/null
+++ b/ui/src/router/modules/stream.js
@@ -0,0 +1,52 @@
+/*
+ * Licensed to Apache Software Foundation (ASF) under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Apache Software Foundation (ASF) licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { BASE_PATH, SCHEMA_TYPES, ROUTE_PARAMS } from '../constants';
+import { createStartRoute, createIndexRuleRoutes, createIndexRuleBindingRoutes 
} from '../routeFactory';
+
+/**
+ * Stream schema routes
+ * Includes: CRUD, index rules, index rule bindings
+ */
+export default {
+  path: `${BASE_PATH}/stream`,
+  name: 'streamHome',
+  redirect: `${BASE_PATH}/stream/start`,
+  component: () => import('@/views/Stream/index.vue'),
+  children: [
+    createStartRoute(SCHEMA_TYPES.STREAM),
+    {
+      path: ROUTE_PARAMS.OPERATOR_READ,
+      name: SCHEMA_TYPES.STREAM,
+      component: () => import('@/views/Stream/stream.vue'),
+    },
+    {
+      path: ROUTE_PARAMS.OPERATOR_CREATE,
+      name: `create-${SCHEMA_TYPES.STREAM}`,
+      component: () => import('@/views/Stream/createEdit.vue'),
+    },
+    {
+      path: ROUTE_PARAMS.OPERATOR_EDIT,
+      name: `edit-${SCHEMA_TYPES.STREAM}`,
+      component: () => import('@/views/Stream/createEdit.vue'),
+    },
+    ...createIndexRuleRoutes(SCHEMA_TYPES.STREAM),
+    ...createIndexRuleBindingRoutes(SCHEMA_TYPES.STREAM),
+  ],
+};
diff --git a/ui/src/router/modules/trace.js b/ui/src/router/modules/trace.js
new file mode 100644
index 00000000..102b26db
--- /dev/null
+++ b/ui/src/router/modules/trace.js
@@ -0,0 +1,52 @@
+/*
+ * Licensed to Apache Software Foundation (ASF) under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Apache Software Foundation (ASF) licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { BASE_PATH, SCHEMA_TYPES, ROUTE_PARAMS } from '../constants';
+import { createStartRoute, createIndexRuleRoutes, createIndexRuleBindingRoutes 
} from '../routeFactory';
+
+/**
+ * Trace schema routes
+ * Includes: CRUD, index rules, index rule bindings
+ */
+export default {
+  path: `${BASE_PATH}/trace`,
+  name: 'traceHome',
+  redirect: `${BASE_PATH}/trace/start`,
+  component: () => import('@/views/Trace/index.vue'),
+  children: [
+    createStartRoute(SCHEMA_TYPES.TRACE),
+    {
+      path: ROUTE_PARAMS.OPERATOR_READ,
+      name: SCHEMA_TYPES.TRACE,
+      component: () => import('@/components/Trace/TraceRead.vue'),
+    },
+    {
+      path: ROUTE_PARAMS.OPERATOR_CREATE,
+      name: `create-${SCHEMA_TYPES.TRACE}`,
+      component: () => import('@/views/Trace/createEdit.vue'),
+    },
+    {
+      path: ROUTE_PARAMS.OPERATOR_EDIT,
+      name: `edit-${SCHEMA_TYPES.TRACE}`,
+      component: () => import('@/views/Trace/createEdit.vue'),
+    },
+    ...createIndexRuleRoutes(SCHEMA_TYPES.TRACE),
+    ...createIndexRuleBindingRoutes(SCHEMA_TYPES.TRACE),
+  ],
+};
diff --git a/ui/src/router/routeFactory.js b/ui/src/router/routeFactory.js
new file mode 100644
index 00000000..649166d5
--- /dev/null
+++ b/ui/src/router/routeFactory.js
@@ -0,0 +1,113 @@
+/*
+ * Licensed to Apache Software Foundation (ASF) under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Apache Software Foundation (ASF) licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { ROUTE_PARAMS } from './constants';
+
+/**
+ * Route Factory Functions
+ *
+ * Generate route configurations with consistent patterns.
+ * All routes use relative paths (nested under parent routes in modules).
+ * CRUD routes are defined inline in modules for Vite static import 
compatibility.
+ */
+
+/**
+ * Creates start page route
+ * @param {string} schemaType - Schema type (stream, measure, trace, property)
+ */
+export function createStartRoute(schemaType) {
+  return {
+    path: 'start',
+    name: `${schemaType}Start`,
+    component: () => import('@/components/Start/index.vue'),
+    meta: { type: schemaType },
+  };
+}
+
+/**
+ * Creates index rule routes (read, create, edit)
+ * @param {string} schemaType - Schema type
+ */
+export function createIndexRuleRoutes(schemaType) {
+  return [
+    {
+      path: `${ROUTE_PARAMS.OPERATOR_READ}`,
+      name: `${schemaType}-index-rule`,
+      component: () => import('@/components/IndexRule/index.vue'),
+    },
+    {
+      path: `${ROUTE_PARAMS.OPERATOR_CREATE}`,
+      name: `${schemaType}-create-index-rule`,
+      component: () => import('@/components/IndexRule/Editor.vue'),
+    },
+    {
+      path: `${ROUTE_PARAMS.OPERATOR_EDIT}`,
+      name: `${schemaType}-edit-index-rule`,
+      component: () => import('@/components/IndexRule/Editor.vue'),
+    },
+  ];
+}
+
+/**
+ * Creates index rule binding routes (read, create, edit)
+ * @param {string} schemaType - Schema type
+ */
+export function createIndexRuleBindingRoutes(schemaType) {
+  return [
+    {
+      path: `${ROUTE_PARAMS.OPERATOR_READ}`,
+      name: `${schemaType}-index-rule-binding`,
+      component: () => import('@/components/IndexRuleBinding/index.vue'),
+    },
+    {
+      path: `${ROUTE_PARAMS.OPERATOR_CREATE}`,
+      name: `${schemaType}-create-index-rule-binding`,
+      component: () => import('@/components/IndexRuleBinding/Editor.vue'),
+    },
+    {
+      path: `${ROUTE_PARAMS.OPERATOR_EDIT}`,
+      name: `${schemaType}-edit-index-rule-binding`,
+      component: () => import('@/components/IndexRuleBinding/Editor.vue'),
+    },
+  ];
+}
+
+/**
+ * Creates TopN aggregation routes (measure only)
+ * @param {string} schemaType - Schema type (typically 'measure')
+ */
+export function createTopNAggRoutes(schemaType) {
+  return [
+    {
+      path: `${ROUTE_PARAMS.OPERATOR_READ}`,
+      name: `${schemaType}-topn-agg`,
+      component: () => import('@/components/TopNAggregation/index.vue'),
+    },
+    {
+      path: `${ROUTE_PARAMS.OPERATOR_CREATE}`,
+      name: `${schemaType}-create-topn-agg`,
+      component: () => import('@/components/TopNAggregation/Editor.vue'),
+    },
+    {
+      path: `${ROUTE_PARAMS.OPERATOR_EDIT}`,
+      name: `${schemaType}-edit-topn-agg`,
+      component: () => import('@/components/TopNAggregation/Editor.vue'),
+    },
+  ];
+}
diff --git a/ui/src/views/Measure/index.vue b/ui/src/views/Measure/index.vue
index d8ad6a73..740366a5 100644
--- a/ui/src/views/Measure/index.vue
+++ b/ui/src/views/Measure/index.vue
@@ -22,9 +22,10 @@
   import TopNav from '@/components/TopNav/index.vue';
   import { reactive } from 'vue';
   import { CatalogToGroupType } from '@/components/common/data';
+  import { ASIDE_WIDTH } from '../constants';
 
   const data = reactive({
-    width: '200px',
+    width: `${ASIDE_WIDTH}px`,
   });
 
   function setWidth(width) {
diff --git a/ui/src/views/Property/index.vue b/ui/src/views/Property/index.vue
index 21ce8902..e0db3e20 100644
--- a/ui/src/views/Property/index.vue
+++ b/ui/src/views/Property/index.vue
@@ -22,9 +22,10 @@
   import GroupTree from '@/components/GroupTree/index.vue';
   import TopNav from '@/components/TopNav/index.vue';
   import { CatalogToGroupType } from '@/components/common/data';
+  import { ASIDE_WIDTH } from '../constants';
 
   const data = reactive({
-    width: '200px',
+    width: `${ASIDE_WIDTH}px`,
   });
 
   function setWidth(width) {
diff --git a/ui/src/views/Stream/index.vue b/ui/src/views/Stream/index.vue
index 724305fa..b50351ac 100644
--- a/ui/src/views/Stream/index.vue
+++ b/ui/src/views/Stream/index.vue
@@ -22,9 +22,10 @@
   import GroupTree from '@/components/GroupTree/index.vue';
   import TopNav from '@/components/TopNav/index.vue';
   import { CatalogToGroupType } from '@/components/common/data';
+  import { ASIDE_WIDTH } from '../constants';
 
   const data = reactive({
-    width: '200px',
+    width: `${ASIDE_WIDTH}px`,
   });
 
   function setWidth(width) {
diff --git a/ui/src/views/Trace/index.vue b/ui/src/views/Trace/index.vue
index 2148334b..c5a117cb 100644
--- a/ui/src/views/Trace/index.vue
+++ b/ui/src/views/Trace/index.vue
@@ -22,9 +22,10 @@
   import GroupTree from '@/components/GroupTree/index.vue';
   import TopNav from '@/components/TopNav/index.vue';
   import { CatalogToGroupType } from '@/components/common/data';
+  import { ASIDE_WIDTH } from '../constants';
 
   const data = reactive({
-    width: '200px',
+    width: `${ASIDE_WIDTH}px`,
   });
 
   function setWidth(width) {
diff --git a/ui/src/views/constants.js b/ui/src/views/constants.js
new file mode 100644
index 00000000..e06a0778
--- /dev/null
+++ b/ui/src/views/constants.js
@@ -0,0 +1,21 @@
+/*
+ * Licensed to Apache Software Foundation (ASF) under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Apache Software Foundation (ASF) licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/** Width of the aside menu */
+export const ASIDE_WIDTH = 280;


Reply via email to