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

wu-sheng pushed a commit to branch refactor/decompose-large-files
in repository https://gitbox.apache.org/repos/asf/skywalking-horizon-ui.git

commit b163842869d11e4c7a2d24aab10dca0825c09627
Author: Wu Sheng <[email protected]>
AuthorDate: Sat Jun 27 07:49:08 2026 +0800

    feat(browser-errors): manual-fire query gate (match Logs)
    
    Browser Errors now stages filter edits (version / page / time) into an 
applied snapshot and fetches only on Run query — a fresh tab shows the "Pick 
your conditions, then click Run query" prompt, and a service switch resets to 
it (clearing the category filter). page + pageSize stay live (paging is a 
direct action). Mirrors the per-layer Logs manual-fire.
---
 CHANGELOG.md                                       |  2 +-
 .../browser-errors/LayerBrowserErrorsView.vue      | 60 ++++++++++++++++++----
 .../layer/browser-errors/useLayerBrowserErrors.ts  |  5 +-
 3 files changed, 56 insertions(+), 11 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index b3c3b00..cd59520 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,7 +26,7 @@ The version line is shared by every package in the monorepo 
(apps + shared packa
 
 ### Logs
 
-- **Logs query on demand, not on every edit.** The per-layer Logs tab and 
cross-layer Log inspect now stage condition changes and fetch only when you 
press **Run query** — a fresh tab shows a "Pick your conditions, then click Run 
query" prompt, and switching service resets to that prompt and clears the level 
/ tag filters, so the previous service's logs never linger under the new one.
+- **Log and browser-error lists query on demand, not on every edit.** The 
per-layer Logs tab, cross-layer Log inspect, and the Browser Errors tab now 
stage condition changes and fetch only when you press **Run query** — a fresh 
tab shows a "Pick your conditions, then click Run query" prompt, and switching 
service resets to that prompt (clearing the level / tag / category filters), so 
the previous service's data never lingers under the new one.
 
 ### Alarms
 
diff --git a/apps/ui/src/layer/browser-errors/LayerBrowserErrorsView.vue 
b/apps/ui/src/layer/browser-errors/LayerBrowserErrorsView.vue
index 91b72f2..6140735 100644
--- a/apps/ui/src/layer/browser-errors/LayerBrowserErrorsView.vue
+++ b/apps/ui/src/layer/browser-errors/LayerBrowserErrorsView.vue
@@ -162,24 +162,65 @@ function clearPage(): void {
   pageQuery.value = '';
 }
 
+// Manual-fire: filter edits (version / page / time) stage into `applied`;
+// the query reads that snapshot, so it fires only on Run query — never the
+// prior service's errors on a fresh tab. page + pageSize stay live (paging
+// is a direct action, not a staged filter).
+const hasQueried = ref(false);
+interface AppliedBrowserConditions {
+  service: string | null;
+  serviceVersionId: string;
+  pagePathId: string;
+  windowMinutes: number;
+  startMs: number | null;
+  endMs: number | null;
+}
+function snapshotConditions(): AppliedBrowserConditions {
+  return {
+    service: serviceName.value,
+    serviceVersionId: selectedVersionId.value,
+    pagePathId: selectedPageId.value,
+    windowMinutes: windowMinutesEffective.value,
+    startMs: startMsRef.value,
+    endMs: endMsRef.value,
+  };
+}
+const applied = ref<AppliedBrowserConditions>(snapshotConditions());
+function applyConditions(): void {
+  applied.value = snapshotConditions();
+}
+
 const { logs, total, reachable, queryError, isFetching, refetch } = 
useLayerBrowserErrors(layerKey, {
-  service: serviceName,
-  serviceVersionId: selectedVersionId,
-  pagePathId: selectedPageId,
+  service: computed(() => applied.value.service),
+  serviceVersionId: computed(() => applied.value.serviceVersionId),
+  pagePathId: computed(() => applied.value.pagePathId),
   category: allCategories,
   page,
   pageSize,
-  windowMinutes: windowMinutesEffective,
-  startMs: startMsRef,
-  endMs: endMsRef,
+  windowMinutes: computed(() => applied.value.windowMinutes),
+  startMs: computed(() => applied.value.startMs),
+  endMs: computed(() => applied.value.endMs),
+  enabled: hasQueried,
 });
+function runQuery(): void {
+  applyConditions();
+  page.value = 1;
+  hasQueried.value = true;
+  void refetch();
+}
 
-// Version/page ids belong to the previous service — drop them on app change.
+// Service switch is a context change → cascade-clear back to the Run-query
+// prompt; never show the prior service's errors under the new one.
 watch(serviceName, () => {
   selectedVersionId.value = '';
   clearPage();
+  hasQueried.value = false;
+  page.value = 1;
+  applyConditions();
 });
-watch([serviceName, windowMinutes, customStart, customEnd, selectedVersionId, 
selectedPageId, pageSize], () => {
+// pageSize is a live pagination control (reset page + refetch); filter edits
+// stage into `applied` and wait for Run query.
+watch(pageSize, () => {
   page.value = 1;
 });
 // `expanded` is an index into `filteredLogs`; drop it when the set changes.
@@ -285,7 +326,7 @@ function loc(row: BrowserErrorRow): string {
             {{ t('Source maps') }}
             <span class="be-maps-count">{{ sourceMaps.length }}</span>
           </button>
-          <button class="sw-btn primary" type="button" :disabled="isFetching" 
@click="refetch()">{{ t('Run query') }}</button>
+          <button class="sw-btn primary" type="button" :disabled="isFetching" 
@click="runQuery">{{ t('Run query') }}</button>
         </div>
       </div>
       <div class="lg-conditions">
@@ -370,6 +411,7 @@ function loc(row: BrowserErrorRow): string {
         </DensityHistogram>
 
         <div v-if="!serviceName" class="lg-empty">{{ t('Select an app to view 
its browser logs.') }}</div>
+        <div v-else-if="!hasQueried" class="lg-empty">{{ t('Pick your 
conditions, then click Run query.') }}</div>
         <div v-else-if="isFetching && logs.length === 0" class="lg-empty">{{ 
t('Reading data…') }}</div>
         <div v-else-if="!reachable" class="lg-empty">{{ t('Backend 
unreachable.') }}<span v-if="queryError"> {{ queryError }}</span></div>
         <div v-else-if="filteredLogs.length === 0" class="lg-empty">
diff --git a/apps/ui/src/layer/browser-errors/useLayerBrowserErrors.ts 
b/apps/ui/src/layer/browser-errors/useLayerBrowserErrors.ts
index d128f2a..c5f8d84 100644
--- a/apps/ui/src/layer/browser-errors/useLayerBrowserErrors.ts
+++ b/apps/ui/src/layer/browser-errors/useLayerBrowserErrors.ts
@@ -36,6 +36,9 @@ export interface BrowserErrorParams {
    *  `windowMinutes`. The BFF applies the OAP timezone offset. */
   startMs?: Ref<number | null>;
   endMs?: Ref<number | null>;
+  /** Gate the query — when false it never runs. Manual-fire holds it until
+   *  the operator presses Run query. Defaults to always-on. */
+  enabled?: Ref<boolean>;
 }
 
 export function useLayerBrowserErrors(layerKey: Ref<string>, params: 
BrowserErrorParams) {
@@ -69,7 +72,7 @@ export function useLayerBrowserErrors(layerKey: Ref<string>, 
params: BrowserErro
         page: params.page.value,
         pageSize: params.pageSize.value,
       }),
-    enabled: computed(() => layerKey.value.length > 0),
+    enabled: computed(() => layerKey.value.length > 0 && (params.enabled ? 
params.enabled.value : true)),
     staleTime: 15_000,
   });
   // No auto-refresh subscription: this is a triage page that owns its time

Reply via email to