Skip to content

feat(datasource): optimize datasource page - #2231

Merged
jsers merged 8 commits into
mainfrom
optimize-datasource-page
Aug 4, 2026
Merged

feat(datasource): optimize datasource page#2231
jsers merged 8 commits into
mainfrom
optimize-datasource-page

Conversation

@710leo

@710leo 710leo commented Jul 31, 2026

Copy link
Copy Markdown
Member
image

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Added datasource health checks with connectivity, freshness, and sample-data guidance.
    • Added post-save guidance for exploring data, creating dashboards or alerts, and editing datasources.
    • Added dashboard and alert template matching and import workflows.
    • Added contextual banners, datasource actions, inspection flows, and safer disable confirmation.
    • Added onboarding dismissal remembered across browser sessions.
  • Bug Fixes

    • Corrected Explorer history links to preserve datasource selection.
    • Improved import validation, error handling, and success feedback.
  • Localization

    • Added translated guidance and template-import text across supported languages.

保存数据源不再直接跳回列表,改为弹出引导弹窗,串起「数据体检 → 下一步动作 → 组件模板一键导入」。

- 新增数据体检 useDataProbe:Prometheus 系数据源在保存后与列表「数据状态」入口实测连通性、
  指标数与最近样本时间,区分 hasData / staleData / noData / unreachable / unsupported 五态
- 新增组件模板匹配面板 TemplateMatch:按后端哨兵指标法的匹配结果展示组件卡片,
  就地勾选导入仪表盘与告警规则,导入时自动预绑定当前数据源
- 抽出 nextActions 作为「下一步动作」的单一事实源,保存弹窗、详情抽屉、列表行共用,
  能力判断由 Cate 清单驱动,不可用的动作置灰并说明原因而非静默隐藏
- 抽出 builtInComponents/AlertRules/ImportForm,集成中心弹窗与模板匹配内联复用同一份导入逻辑,
  顺带支持导入时就地选择通知规则
- 探索器新增体检落地横幅 ProbeBanner,仪表盘/告警规则列表新增引导承接横幅 GuideLandingBanner
- 数据源列表行内操作补充「数据状态」与「编辑」,停用开关加二次确认
- 修复探索器 history.replace 拼 search 时漏了 data_source_id 参数名
- 新手引导支持「不再显示」,完成标记从 sessionStorage 改为 localStorage
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Changes

Datasource and onboarding experience

Layer / File(s) Summary
Onboarding dismissal persistence
src/components/OnboardingProgress/*, src/pages/landing/locale/*
Adds localized dismissal and durable onboarding completion in localStorage.
Datasource actions and health probing
src/pages/datasource/nextActions.*, src/pages/datasource/utils/*
Adds datasource actions, Prometheus probing, journey tracking, retries, persistence, and tests.
Datasource guidance and navigation
src/pages/datasource/*, src/pages/alertRules/List/index.tsx, src/pages/dashboard/List/index.tsx, src/components/PromGraphCpt/index.tsx
Adds save-result guidance, probe states, datasource actions, landing banners, localization, and banner rendering support.
Template matching and reusable imports
src/components/TemplateMatch/*, src/pages/builtInComponents/AlertRules/*, src/pages/builtInComponents/Dashboards/Import.tsx, src/components/ModalHOC.tsx
Adds dashboard and alert template matching and import flows, and extracts alert-rule import logic into ImportForm.
Explorer probe integration
src/pages/explorer/*
Adds probe-result banners to Prometheus Explorer and fixes the datasource query parameter.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DatasourceForm
  participant NextStepModal
  participant useDataProbe
  participant TemplateMatchPanel
  participant ImportModal
  DatasourceForm->>NextStepModal: show saved datasource result
  NextStepModal->>useDataProbe: probe datasource
  useDataProbe-->>NextStepModal: return verification state
  NextStepModal->>TemplateMatchPanel: open template matching
  TemplateMatchPanel->>ImportModal: open selected template
  ImportModal-->>DatasourceForm: complete dashboard or alert import
Loading

Possibly related PRs

  • n9e/fe#2138: Introduces the onboarding components extended by this PR.
  • n9e/fe#2163: Also modifies onboarding popover and progress behavior.
  • n9e/fe#2230: Overlaps with onboarding progress components, hook behavior, and localization.

Suggested reviewers: jsers

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately identifies the pull request as a datasource feature update and summarizes the main datasource-page guidance changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch optimize-datasource-page

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

🧹 Nitpick comments (5)
src/pages/builtInComponents/AlertRules/ImportForm.tsx (1)

38-41: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Type the context-derived props.

busiGroups, groupedDatasourceList, reloadGroupedDatasourceList, and datasourceCateOptions are typed any. This file is a new shared contract used by two hosts, so the loss of type safety now spreads. Reuse the types exported by CommonStateContext.

As per coding guidelines: "Declare component Props explicitly with interface and avoid any."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/builtInComponents/AlertRules/ImportForm.tsx` around lines 38 - 41,
Replace the any types on the shared props interface in ImportForm with the
corresponding types exported by CommonStateContext for busiGroups,
groupedDatasourceList, reloadGroupedDatasourceList, and datasourceCateOptions.
Keep the component Props explicitly declared as an interface and preserve the
existing shared contract for both hosts.

Source: Coding guidelines

src/pages/explorer/Prometheus/index.tsx (1)

74-75: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Consider hiding the banner when the datasource changes.

probeBannerVisible stays true when the user selects another datasource in the explorer. ProbeBanner then reads the probe result of the new datasource and can show a success banner that the user did not ask for, and it also records explored_at for that datasource. A selection change is an explicit takeover, so treat it like the query change at Line 156.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/explorer/Prometheus/index.tsx` around lines 74 - 75, Update the
datasource selection handler in the Prometheus explorer to set
probeBannerVisible to false whenever the datasource changes, matching the
existing takeover behavior in the query-change handler. Use the visible
probeBannerVisible state and datasource-change logic, while preserving the
initial __from === 'ds_verify' && panelIdx === 0 condition.
src/pages/datasource/locale/zh_HK.ts (1)

124-180: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use one term for datasource in this file.

The new keys use 資料來源. The existing keys use 數據源 (Line 2 數據源管理, Line 6 還沒有數據源). The same UI now shows two terms for one concept. Pick one term and apply it to the new keys.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/datasource/locale/zh_HK.ts` around lines 124 - 180, Use the file’s
existing `數據源` terminology consistently in the newly added datasource-related
translations, replacing `資料來源` in keys such as `unreachable`, `guide_landing`,
and the associated descriptions and actions while preserving the surrounding
Hong Kong Chinese wording.
src/components/TemplateMatch/services.ts (1)

45-54: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Consider limiting the retry to the "client not ready" error.

The comment states that the retry covers the roughly 1 second rebuild window after a save. The current code retries after any rejection, including authorization failures and timeouts. That adds one wasted request and about 1.8 s before the caller reaches the empty state. Inspect the rejection reason before retrying.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/TemplateMatch/services.ts` around lines 45 - 54, Update
postTemplateMatch so the delayed retry occurs only when doTemplateMatch rejects
with the specific “client not ready” error; propagate authorization failures,
timeouts, and other rejection reasons immediately. Inspect the rejection reason
in the existing catch before scheduling the setTimeout retry, preserving the
current retry delay and result handling for the eligible error.
src/components/TemplateMatch/index.tsx (1)

111-111: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Replace the hardcoded rgba fallback with a theme value.

The class contains rgba(0,0,0,0.09) as the --fc-shadow-md fallback. A hardcoded shadow color does not follow the dark theme. Define the shadow in the theme variables, or drop the fallback so the theme variable is the single source.

As per coding guidelines: "Use theme and color values from src/theme/variable.css and the existing theme system instead of magic color literals."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/TemplateMatch/index.tsx` at line 111, Update the className in
the TemplateMatch component to remove the hardcoded rgba shadow fallback and use
the existing theme variable system from variable.css as the sole shadow source.
Preserve the current hover shadow behavior while ensuring it follows dark and
light themes.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/OnboardingProgress/PopoverContent.tsx`:
- Around line 55-61: Replace the dismissal <a> in the onDismiss rendering within
PopoverContent with a button using type="button", preserving the existing
className, onDismiss handler, and translated label so the control is keyboard
accessible.

In `@src/components/OnboardingProgress/useOnboardingProgress.ts`:
- Around line 33-35: Update useOnboardingProgress to expose a separate dismissed
state derived from and restored by the DONE_DETECT marker, rather than treating
dismissal as datasource completion. Ensure dismiss() sets and persists this
state, and update OnboardingProgressBadge and other onboarding surfaces to hide
when dismissed is true, including when no datasource exists.
- Around line 111-114: Update the onboarding progress effect around the
ONBOARDING_DONE_KEY localStorage read to catch storage access errors, using the
same fallback pattern as dismiss() and the completion write. On failure,
continue with session-only detection and ensure probeOnboarding() still runs so
loading state is handled.

In `@src/components/TemplateMatch/ImportModal.tsx`:
- Around line 118-167: The importDashboards function must tolerate partial
createDashboard failures instead of aborting on the first rejection. Replace the
Promise.all flow with Promise.allSettled, associate each result with its
selected dashboard, mark fulfilled dashboards via setDashImported, and report
rejected or returned-error failures in the modal while preserving retryability
for unsuccessful items.
- Around line 255-266: Ensure the mounted ImportForm reflects later bgid changes
instead of relying on antd initialValues, which only apply on mount. Update the
ImportForm integration and its initialBgid handling so a bgid change either
remounts the form or resets the form before setting the replacement field
values, preserving existing behavior for other initial values.

In `@src/pages/builtInComponents/AlertRules/ImportForm.tsx`:
- Line 71: Replace the lodash `_.isArray` checks around `dataList` and the
additional occurrences near the corresponding parsed JSON handling with native
`Array.isArray` checks, preserving the existing narrowing and fallback behavior.
- Around line 167-186: Add rejection handling to the createRule promise in the
import submission flow, alongside its existing then callback. Show an
appropriate user-facing error through the established Modal or message pattern
and ensure the rejection is handled without disrupting the existing success and
per-rule failure behavior.

In `@src/pages/datasource/components/NextActionButton.tsx`:
- Around line 33-50: Update the rendering logic in NextActionButton so enabled
actions without a url are not treated as disabled or unsupported. Preserve the
existing linked-button behavior for enabled actions with a url, and ensure the
no-standalone-page case renders without the disabled tooltip/button path, using
the component’s established null or inline-rendering behavior.

In `@src/pages/datasource/components/NextStepModal.tsx`:
- Around line 264-282: Update the non-inspect title in NextStepModal’s title
rendering to use locale translation keys such as saved_title and updated_title,
passing cateLabel and name as interpolation values instead of hardcoding 「」
around name. Add the corresponding keys to each locale with that language’s
appropriate quotation marks, while preserving the existing saved-versus-updated
selection and inspect-mode title.

In `@src/pages/datasource/components/TableSource/index.tsx`:
- Around line 181-198: Add error handling to the enable branch in the status
update handler: ensure the promise returned by doUpdate and
updateDataSourceStatus surfaces failures through the existing user-facing error
mechanism, while preserving success messaging and refresh behavior. Keep the
disable confirmation flow unchanged.

In `@src/pages/datasource/Detail.tsx`:
- Around line 28-30: Update the action derivation in the component around
`actions` and `exploreAction` so `getNextActions(cate, data?.id, isPlus)` is
called only once. Derive `exploreAction` by finding the primary explore action
directly from the existing `actions` array, matching the approach used in
`NextStepModal.tsx`, and remove the redundant `getPrimaryExploreAction` call.

In `@src/pages/datasource/Form.tsx`:
- Around line 179-186: The NextStepModal component is missing the `disabled`
prop, which causes it to report a disabled datasource as "unreachable" instead
of "disabled" when the modal probes it. Add the `disabled` prop to the
NextStepModal invocation using the result object's status field, mirroring the
pattern used in TableSource/index.tsx with `disabled={guideRecord.status !==
'enabled'}`, so the modal receives the correct disabled state and does not
attempt verification on disabled datasources.

In `@src/pages/datasource/locale/zh_HK.ts`:
- Around line 131-142: Update the added zh_HK translation block, including the
entries around probing and lines 181–205, to use Traditional Hong Kong Chinese
consistently. Replace all Simplified glyphs with the established zh_HK
terminology, including 檢查, 意味著, 暫不支援, 這個, 內置, 瀏覽, 建立, 匯入/導入, 選, and 當前, while
preserving the existing translation keys and interpolation placeholders.

In `@src/pages/explorer/components/ProbeBanner/index.tsx`:
- Around line 64-69: Update the two Link components in ProbeBanner by appending
the guided-landing context query parameters to the to prop values. Modify the
Link to '/dashboards' and the Link to '/alert-rules' to include the query string
__from=ds_guide&data_source_id=${datasourceId} so that the GuideLandingBanner
and actionMap receive the expected parameters when users navigate to these
pages.

---

Nitpick comments:
In `@src/components/TemplateMatch/index.tsx`:
- Line 111: Update the className in the TemplateMatch component to remove the
hardcoded rgba shadow fallback and use the existing theme variable system from
variable.css as the sole shadow source. Preserve the current hover shadow
behavior while ensuring it follows dark and light themes.

In `@src/components/TemplateMatch/services.ts`:
- Around line 45-54: Update postTemplateMatch so the delayed retry occurs only
when doTemplateMatch rejects with the specific “client not ready” error;
propagate authorization failures, timeouts, and other rejection reasons
immediately. Inspect the rejection reason in the existing catch before
scheduling the setTimeout retry, preserving the current retry delay and result
handling for the eligible error.

In `@src/pages/builtInComponents/AlertRules/ImportForm.tsx`:
- Around line 38-41: Replace the any types on the shared props interface in
ImportForm with the corresponding types exported by CommonStateContext for
busiGroups, groupedDatasourceList, reloadGroupedDatasourceList, and
datasourceCateOptions. Keep the component Props explicitly declared as an
interface and preserve the existing shared contract for both hosts.

In `@src/pages/datasource/locale/zh_HK.ts`:
- Around line 124-180: Use the file’s existing `數據源` terminology consistently in
the newly added datasource-related translations, replacing `資料來源` in keys such
as `unreachable`, `guide_landing`, and the associated descriptions and actions
while preserving the surrounding Hong Kong Chinese wording.

In `@src/pages/explorer/Prometheus/index.tsx`:
- Around line 74-75: Update the datasource selection handler in the Prometheus
explorer to set probeBannerVisible to false whenever the datasource changes,
matching the existing takeover behavior in the query-change handler. Use the
visible probeBannerVisible state and datasource-change logic, while preserving
the initial __from === 'ds_verify' && panelIdx === 0 condition.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: be53dd47-c744-48a5-b833-b4d818923b98

📥 Commits

Reviewing files that changed from the base of the PR and between 0a1d05c and 9b92522.

📒 Files selected for processing (38)
  • src/components/OnboardingProgress/PopoverContent.tsx
  • src/components/OnboardingProgress/index.tsx
  • src/components/OnboardingProgress/useOnboardingProgress.ts
  • src/components/PromGraphCpt/index.tsx
  • src/components/TemplateMatch/ImportModal.tsx
  • src/components/TemplateMatch/index.tsx
  • src/components/TemplateMatch/services.ts
  • src/pages/alertRules/List/index.tsx
  • src/pages/builtInComponents/AlertRules/Import.tsx
  • src/pages/builtInComponents/AlertRules/ImportForm.tsx
  • src/pages/builtInComponents/Dashboards/Import.tsx
  • src/pages/dashboard/List/index.tsx
  • src/pages/datasource/Detail.tsx
  • src/pages/datasource/Form.tsx
  • src/pages/datasource/components/GuideLandingBanner.tsx
  • src/pages/datasource/components/NextActionButton.tsx
  • src/pages/datasource/components/NextStepModal.tsx
  • src/pages/datasource/components/TableSource/index.tsx
  • src/pages/datasource/index.less
  • src/pages/datasource/index.tsx
  • src/pages/datasource/locale/en_US.ts
  • src/pages/datasource/locale/ja_JP.ts
  • src/pages/datasource/locale/ru_RU.ts
  • src/pages/datasource/locale/zh_CN.ts
  • src/pages/datasource/locale/zh_HK.ts
  • src/pages/datasource/nextActions.test.ts
  • src/pages/datasource/nextActions.ts
  • src/pages/datasource/utils/journey.ts
  • src/pages/datasource/utils/useDataProbe.test.ts
  • src/pages/datasource/utils/useDataProbe.ts
  • src/pages/explorer/Explorer.tsx
  • src/pages/explorer/Prometheus/index.tsx
  • src/pages/explorer/components/ProbeBanner/index.tsx
  • src/pages/landing/locale/en_US.ts
  • src/pages/landing/locale/ja_JP.ts
  • src/pages/landing/locale/ru_RU.ts
  • src/pages/landing/locale/zh_CN.ts
  • src/pages/landing/locale/zh_HK.ts
💤 Files with no reviewable changes (1)
  • src/pages/datasource/index.less

Comment on lines +55 to +61
{onDismiss && (
<div className='text-right mt-1'>
<a className='text-[var(--fc-text-4)] text-xs' onClick={onDismiss}>
{t('onboarding.dismiss')}
</a>
</div>
)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use a keyboard-accessible control for dismissal.

The <a> element has no href, role, or keyboard handler. Keyboard users cannot focus or activate it. Replace it with <button type='button'> and preserve the existing styles.

Proposed fix
-          <a className='text-[var(--fc-text-4)] text-xs' onClick={onDismiss}>
+          <button
+            type='button'
+            className='text-[var(--fc-text-4)] text-xs bg-transparent border-0 p-0 cursor-pointer'
+            onClick={onDismiss}
+          >
             {t('onboarding.dismiss')}
-          </a>
+          </button>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{onDismiss && (
<div className='text-right mt-1'>
<a className='text-[var(--fc-text-4)] text-xs' onClick={onDismiss}>
{t('onboarding.dismiss')}
</a>
</div>
)}
{onDismiss && (
<div className='text-right mt-1'>
<button
type='button'
className='text-[var(--fc-text-4)] text-xs bg-transparent border-0 p-0 cursor-pointer'
onClick={onDismiss}
>
{t('onboarding.dismiss')}
</button>
</div>
)}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/OnboardingProgress/PopoverContent.tsx` around lines 55 - 61,
Replace the dismissal <a> in the onDismiss rendering within PopoverContent with
a button using type="button", preserving the existing className, onDismiss
handler, and translated label so the control is keyboard accessible.

Comment thread src/components/OnboardingProgress/useOnboardingProgress.ts
Comment on lines +111 to 114
if (localStorage.getItem(ONBOARDING_DONE_KEY)) {
setDetect(DONE_DETECT);
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and inspect the target hook, plus local storage usage/fallback patterns nearby.
fd -a 'useOnboardingProgress\.ts$' . | sed 's#^\./##'
file="$(fd 'useOnboardingProgress\.ts$' . | head -n 1)"
echo "---- file: $file ----"
wc -l "$file"
sed -n '1,220p' "$file" | cat -n

echo "---- storage fallback occurrences ----"
rg -n "localStorage|storageFallback|catch\\(|localStorage.*getItem|setItem|removeItem" src/components/OnboardingProgress src -g '*.ts' -g '*.tsx' | head -n 200

Repository: n9e/fe

Length of output: 28852


🌐 Web query:

MDN localStorage getItem throws SecurityError quotaExceededDocumentDomain

💡 Result:

When working with the Web Storage API, a SecurityError can occur when attempting to access localStorage [1][2]. While the specific error "quotaExceededDocumentDomain" is not a standard error name, SecurityError exceptions are frequently encountered in scenarios involving restricted access or policy violations [3]. Common causes for a SecurityError when accessing localStorage include: 1. Browser Privacy Settings: Users may configure their browser to block third-party cookies or site data entirely, which can lead to SecurityError exceptions when scripts attempt to access localStorage [1][4]. 2. Third-Party IFrame Restrictions: Accessing localStorage from a cross-origin iframe is often restricted, especially if the browser is configured to block third-party storage access [5][6]. 3. Invalid or Opaque Origins: Attempting to use localStorage in contexts with opaque origins (e.g., certain sandboxed iframes, or data: URLs) will result in a SecurityError [1][3]. 4. Policy Decisions: The browser may enforce policies that prevent specific pages or origins from persisting data [1][3]. Note that QuotaExceededError is a distinct exception thrown when the storage limit for an origin has been reached [3][7]. To robustly handle these potential issues, developers should always wrap localStorage access in try-catch blocks to prevent script execution from failing unexpectedly [7][8]. A common practice for feature detection and availability checking is to attempt a test write and read operation within a try-catch block [7][9]. { "top_results": [1][3][7][4][5] }

Citations:


Handle storage-read failures.

localStorage.getItem(ONBOARDING_DONE_KEY) can throw when storage is blocked or unavailable. If it throws at src/components/OnboardingProgress/useOnboardingProgress.ts:111, the effect exits before probeOnboarding() runs and loading state is ignored. Wrap this read in the same try/catch fallback used by dismiss() and the completion write, then fall back to session-only detection.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/OnboardingProgress/useOnboardingProgress.ts` around lines 111
- 114, Update the onboarding progress effect around the ONBOARDING_DONE_KEY
localStorage read to catch storage access errors, using the same fallback
pattern as dismiss() and the completion write. On failure, continue with
session-only detection and ensure probeOnboarding() still runs so loading state
is handled.

Comment on lines +118 to +167
const importDashboards = () => {
if (!bgid) return;
setImporting(true);
getPayloads<PayloadLike[]>({ component_id: entry.component_id, type: TypeEnum.dashboard })
.then((payloads) => {
const selected = _.filter(payloads, (p) => _.includes(_.map(dashChecked, String), String(p.uuid)));
if (_.isEmpty(selected)) return undefined;
return Promise.all(
_.map(selected, (p) => {
const board = JSON.parse(p.content);
return createDashboard(bgid, { ...board, configs: JSON.stringify(board.configs) });
}),
).then((res) => {
const errs = _.compact(_.map(res, 'err'));
if (!_.isEmpty(errs)) {
Modal.error({
title: t('tpl_match.import_failed'),
content: (
<div>
{_.map(_.uniq(errs), (e) => (
<div key={e}>{e}</div>
))}
</div>
),
});
return undefined;
}
// 弹窗不关,用户可以接着导告警;已导入的置灰以免重复点出多份副本
setDashImported((prev) =>
_.union(
prev,
_.map(selected, (p) => p.uuid),
),
);
setDashChecked([]);
markDsJourney(datasourceId, 'dashboard_created_at');
onImported?.('dashboard');
return undefined;
});
})
.catch((e) => {
// 模板拉取失败、或某份模板 content 不是合法 JSON(JSON.parse 同步抛)都会走到这里,
// 不兜住的话按钮只是恢复可点,用户看不到任何反馈也留不下排查线索
console.error(e);
message.error(t('tpl_match.import_error'));
})
.finally(() => {
setImporting(false);
});
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Report partial dashboard import failures.

Promise.all rejects on the first rejected createDashboard call. The catch at Line 158 then shows a single generic error, and setDashImported never records the dashboards that were created. If the user retries, the succeeded dashboards get duplicated. Use Promise.allSettled, then mark the fulfilled items as imported and list the failures.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/TemplateMatch/ImportModal.tsx` around lines 118 - 167, The
importDashboards function must tolerate partial createDashboard failures instead
of aborting on the first rejection. Replace the Promise.all flow with
Promise.allSettled, associate each result with its selected dashboard, mark
fulfilled dashboards via setDashImported, and report rejected or returned-error
failures in the modal while preserving retryability for unsuccessful items.

Comment on lines +255 to +266
<ImportForm
data={alertData}
busiGroups={busiGroups}
groupedDatasourceList={groupedDatasourceList}
reloadGroupedDatasourceList={reloadGroupedDatasourceList}
datasourceCateOptions={datasourceCateOptions}
initialDatasourceQueries={boundDatasourceQueries}
contextBound
initialBgid={bgid}
notificationRulesAuthorized={notificationRulesAuthorized}
submitText={t('tpl_match.import_alerts_btn', { count: activeChecked.length })}
submitDisabled={_.isEmpty(activeChecked) || alertPayloadsLoading}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

initialBgid changes do not reach the mounted ImportForm.

ImportForm passes initialBgid through antd initialValues, which apply only on mount. Both tab panes render at the same time, so ImportForm mounts before the user picks a business group in the dashboards tab at Line 183. After that pick, the alerts tab still shows an empty business group field. Force a remount when bgid changes, or reset and set the field inside ImportForm.

As per coding guidelines: "Because form.setFieldsValue performs incremental updates, reset the form before setting values when full replacement is required."

🛠️ Proposed fix
             <ImportForm
+              key={`${activeCate ?? ''}-${bgid ?? ''}`}
               data={alertData}
               busiGroups={busiGroups}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<ImportForm
data={alertData}
busiGroups={busiGroups}
groupedDatasourceList={groupedDatasourceList}
reloadGroupedDatasourceList={reloadGroupedDatasourceList}
datasourceCateOptions={datasourceCateOptions}
initialDatasourceQueries={boundDatasourceQueries}
contextBound
initialBgid={bgid}
notificationRulesAuthorized={notificationRulesAuthorized}
submitText={t('tpl_match.import_alerts_btn', { count: activeChecked.length })}
submitDisabled={_.isEmpty(activeChecked) || alertPayloadsLoading}
<ImportForm
key={`${activeCate ?? ''}-${bgid ?? ''}`}
data={alertData}
busiGroups={busiGroups}
groupedDatasourceList={groupedDatasourceList}
reloadGroupedDatasourceList={reloadGroupedDatasourceList}
datasourceCateOptions={datasourceCateOptions}
initialDatasourceQueries={boundDatasourceQueries}
contextBound
initialBgid={bgid}
notificationRulesAuthorized={notificationRulesAuthorized}
submitText={t('tpl_match.import_alerts_btn', { count: activeChecked.length })}
submitDisabled={_.isEmpty(activeChecked) || alertPayloadsLoading}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/TemplateMatch/ImportModal.tsx` around lines 255 - 266, Ensure
the mounted ImportForm reflects later bgid changes instead of relying on antd
initialValues, which only apply on mount. Update the ImportForm integration and
its initialBgid handling so a bgid change either remounts the form or resets the
form before setting the replacement field values, preserving existing behavior
for other initial values.

Source: Coding guidelines

Comment thread src/pages/datasource/components/TableSource/index.tsx
Comment on lines +28 to +30
const cate = useMemo(() => _.find(allCates, { value: data?.plugin_type }) as Cate | undefined, [data?.plugin_type]);
const actions = useMemo(() => getNextActions(cate, data?.id, isPlus), [cate, data?.id, isPlus]);
const exploreAction = useMemo(() => getPrimaryExploreAction(cate, data?.id, isPlus), [cate, data?.id, isPlus]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Avoid computing getNextActions twice.

actions and exploreAction both derive from getNextActions(cate, data?.id, isPlus): getPrimaryExploreAction calls getNextActions again internally with the same arguments. NextStepModal.tsx avoids this duplicate work by deriving its explore action with _.find directly on its own actions array. Do the same here to avoid the redundant call.

♻️ Proposed fix
   const actions = useMemo(() => getNextActions(cate, data?.id, isPlus), [cate, data?.id, isPlus]);
-  const exploreAction = useMemo(() => getPrimaryExploreAction(cate, data?.id, isPlus), [cate, data?.id, isPlus]);
+  const exploreAction = useMemo(() => _.find(actions, (a) => a.enabled && (a.key === 'explore_metric' || a.key === 'explore_log')), [actions]);

As per coding guidelines: "Within a component, avoid repeatedly calling the same side-effectful transformation function; extract and reuse its result."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const cate = useMemo(() => _.find(allCates, { value: data?.plugin_type }) as Cate | undefined, [data?.plugin_type]);
const actions = useMemo(() => getNextActions(cate, data?.id, isPlus), [cate, data?.id, isPlus]);
const exploreAction = useMemo(() => getPrimaryExploreAction(cate, data?.id, isPlus), [cate, data?.id, isPlus]);
const cate = useMemo(() => _.find(allCates, { value: data?.plugin_type }) as Cate | undefined, [data?.plugin_type]);
const actions = useMemo(() => getNextActions(cate, data?.id, isPlus), [cate, data?.id, isPlus]);
const exploreAction = useMemo(() => _.find(actions, (a) => a.enabled && (a.key === 'explore_metric' || a.key === 'explore_log')), [actions]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/datasource/Detail.tsx` around lines 28 - 30, Update the action
derivation in the component around `actions` and `exploreAction` so
`getNextActions(cate, data?.id, isPlus)` is called only once. Derive
`exploreAction` by finding the primary explore action directly from the existing
`actions` array, matching the approach used in `NextStepModal.tsx`, and remove
the redundant `getPrimaryExploreAction` call.

Source: Coding guidelines

Comment on lines +179 to +186
{result && (
<NextStepModal
datasourceId={result.id}
pluginType={type}
name={result.name}
mode={action === 'edit' ? 'updated' : 'saved'}
verification={result.verification}
onClose={() => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Pass disabled to NextStepModal so edited disabled datasources do not get misreported.

NextStepModal does not receive a disabled prop here. For action === 'edit', the save request sends is_enable: undefined, so an already-disabled datasource stays disabled after this save. NextStepModal's own disabled prop documents this exact case: a disabled datasource's queries must fail, and this must not get reported as "unreachable". Without this prop, the modal probes a disabled datasource and can show an incorrect "unreachable" state instead of the correct "disabled" state.

TableSource/index.tsx passes disabled={guideRecord.status !== 'enabled'} for the same modal in its "inspect" flow. Mirror that here using the fetched data.status.

🩹 Proposed fix
       {result && (
         <NextStepModal
           datasourceId={result.id}
           pluginType={type}
           name={result.name}
           mode={action === 'edit' ? 'updated' : 'saved'}
           verification={result.verification}
+          disabled={data?.status === 'disabled'}
           onClose={() => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{result && (
<NextStepModal
datasourceId={result.id}
pluginType={type}
name={result.name}
mode={action === 'edit' ? 'updated' : 'saved'}
verification={result.verification}
onClose={() => {
{result && (
<NextStepModal
datasourceId={result.id}
pluginType={type}
name={result.name}
mode={action === 'edit' ? 'updated' : 'saved'}
verification={result.verification}
disabled={data?.status === 'disabled'}
onClose={() => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/datasource/Form.tsx` around lines 179 - 186, The NextStepModal
component is missing the `disabled` prop, which causes it to report a disabled
datasource as "unreachable" instead of "disabled" when the modal probes it. Add
the `disabled` prop to the NextStepModal invocation using the result object's
status field, mirroring the pattern used in TableSource/index.tsx with
`disabled={guideRecord.status !== 'enabled'}`, so the modal receives the correct
disabled state and does not attempt verification on disabled datasources.

Comment on lines +131 to +142
probing: '正在檢查資料來源…',
has_data: '數據正常',
has_data_desc: '發現 {{count}} 個指標 · 最近數據 {{ago}} · 查詢耗時 {{ms}}ms',
sample_metric: '示例指標',
no_data: '連接正常,但沒有發現任何指標',
no_data_desc: '這通常意味着採集器還沒開始往這裏寫數據,而不是資料來源配置有問題。',
stale_data: '歷史有數據,近期沒有新寫入',
stale_data_desc: '指標存在,但最近沒有新樣本;請檢查採集器是否仍在運行。',
unreachable: '無法連接資料來源',
unreachable_desc: '請檢查地址、認證資訊與網路連通性後重試。',
probe_unsupported: '該類型資料來源暫不支持數據體檢',
probe_unsupported_desc: '連通性與數據情況請到探索器裡實際查一次。',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Replace Simplified Chinese characters in the zh_HK bundle.

Several new values contain Simplified characters. Examples:

  • Line 131: 正在检查资料来源…檢查.
  • Line 136: 意味着意味著.
  • Line 141 and Line 155: 暂不支持暫不支援 (see auth.not-support at Line 32).
  • Line 182: 已在这个资料源里发现已在這個資料來源裡發現.
  • Line 183: 未匹配到内置组件模板,可到集成中心浏览,或直接创建仪表盘。未匹配到內置組件模板,可到集成中心瀏覽,或直接建立儀表盤。
  • Lines 191-204: 导入, , 当前匯入/導入, , 當前.

Please sweep the whole added block for Simplified glyphs.

Also applies to: 181-205

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/datasource/locale/zh_HK.ts` around lines 131 - 142, Update the
added zh_HK translation block, including the entries around probing and lines
181–205, to use Traditional Hong Kong Chinese consistently. Replace all
Simplified glyphs with the established zh_HK terminology, including 檢查, 意味著,
暫不支援, 這個, 內置, 瀏覽, 建立, 匯入/導入, 選, and 當前, while preserving the existing
translation keys and interpolation placeholders.

Comment on lines +64 to +69
<Link to='/dashboards' target='_blank'>
{t('result.create_dashboard')}
</Link>
<Link to='/alert-rules' target='_blank'>
{t('result.create_alert')}
</Link>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Compare guided-landing link parameters across the datasource result page and the probe banner.
set -uo pipefail

echo '--- GUIDE_LANDING_FROM definition and usages ---'
rg -nP -g '**/*.ts' -g '**/*.tsx' -g '!**/node_modules/**' -C 3 'GUIDE_LANDING_FROM'

echo '--- links to /dashboards and /alert-rules that carry __from ---'
rg -nP -g '**/*.tsx' -g '!**/node_modules/**' -C 3 "__from=" src/pages/datasource src/pages/explorer

Repository: n9e/fe

Length of output: 193


🏁 Script executed:

#!/bin/bash
set -u

echo '--- repo files matching ProbeBanner and GuideLandingBanner ---'
fd -i 'ProbeBanner|GuideLandingBanner' . || true

echo '--- git status/stat ---'
git diff --stat || true

echo '--- broad searches relevant terms ---'
for term in "GuideLandingBanner" "GUIDE_LANDING_FROM" "__from=" "data_source_id" "create_dashboard" "create_alert"; do
  echo "### $term"
  rg -n "$term" -S src packages 2>/dev/null || true
done

Repository: n9e/fe

Length of output: 12747


🏁 Script executed:

#!/bin/bash
set -u

echo '--- ProbeBanner outline ---'
ast-grep outline src/pages/explorer/components/ProbeBanner/index.tsx || true

echo '--- ProbeBanner relevant lines ---'
sed -n '1,120p' src/pages/explorer/components/ProbeBanner/index.tsx

echo '--- GenerateNextActionButton/ProbeBanner imports and NextActionButton implementation ---'
rg -n -g '**/*.ts' -g '**/*.tsx' -C 4 'GenerateNextActionButton|function NextActionButton|const NextActionButton|export .*NextActionButton|handleNextAction' src/pages src/components 2>/dev/null || true

Repository: n9e/fe

Length of output: 4229


🏁 Script executed:

#!/bin/bash
set -u

echo '--- ProbeBanner parent usage in Prometheus explorer ---'
rg -n -g '**/*.ts' -g '**/*.tsx' -C 8 'ProbeBanner|<ProbeBanner|export function readProbeResult|readProbeResult(' src/pages/explorer/src 2>/dev/null || true

echo '--- datasource nextActions implementation and tests ---'
sed -n '1,100p' src/pages/datasource/nextActions.ts
sed -n '60,85p' src/pages/datasource/nextActions.test.ts

echo '--- GuideLandingBanner implementation ---'
sed -n '1,80p' src/pages/datasource/components/GuideLandingBanner.tsx

echo '--- list page mounting of GuideLandingBanner ---'
sed -n '130,150p' src/pages/dashboard/List/index.tsx
sed -n '96,112p' src/pages/alertRules/List/index.tsx

Repository: n9e/fe

Length of output: 8548


Pass the guided-landing context on the ProbeBanner links.

The ProbeBanner current creates /dashboards and /alert-rules without query parameters, while GuideLandingBanner only renders when the URL contains __from=ds_guide and data_source_id, and actionMap expects exactly those links. Add __from=ds_guide&data_source_id=${datasourceId} to both to values so the landing banner and datasource name are shown after the user clicks.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/explorer/components/ProbeBanner/index.tsx` around lines 64 - 69,
Update the two Link components in ProbeBanner by appending the guided-landing
context query parameters to the to prop values. Modify the Link to '/dashboards'
and the Link to '/alert-rules' to include the query string
__from=ds_guide&data_source_id=${datasourceId} so that the GuideLandingBanner
and actionMap receive the expected parameters when users navigate to these
pages.

…ial import accounting

Addresses the blocking findings from the review of the datasource guidance work.

useDataProbe: the label values request carried no time range, so it returned
every metric name in the retention period; the verdict for the whole datasource
was then derived from a single sampled metric. One decommissioned exporter was
enough to report a healthy datasource as noData/staleData, which also hid the
template match panel. Now the metric list is fetched within a time window, and
freshness is measured across several candidate metrics instead of one, so
hasData always carries a real last-sample timestamp.

nextActions: create_dashboard ignored the graphPro gate that explore_metric and
create_alert already applied. The dashboard panel datasource selector filters on
`dashboard === true && (graphPro ? IS_PLUS : true)`, so on the open source build
the guidance sent users to a dashboard where ck/mysql/pgsql/doris/opensearch
could never be selected. The action is now gated and marked pro_only.

TemplateMatch/ImportModal: a partially failed dashboard import returned early
without recording the ones that had already been created. Since Board.Add
rejects duplicate name+group_id, every retry failed on those and the remaining
templates could never be imported. Results are now tracked per dashboard,
failures list the dashboard name, and uuid membership is compared as strings
because the payload and match APIs disagree on its type.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/TemplateMatch/ImportModal.tsx (1)

121-180: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Isolate JSON.parse failures per dashboard to preserve partial-import success.

createDashboard (in src/pages/builtInComponents/Dashboards/services.ts) catches its own errors and resolves { err } rather than rejecting, so wrapping calls in Promise.all no longer aborts the whole batch on a single failed create. This resolves the mechanism flagged in the earlier review comment.

However, JSON.parse(p.content) at Line 133 runs synchronously inside _.map, before Promise.all starts any request. If one selected payload has malformed JSON, _.map throws synchronously, and the whole call aborts before any createDashboard request fires. None of the selected dashboards get created, not just the one with bad content. The user only sees the generic tpl_match.import_error message from the outer .catch at Lines 172-176, which is the exact scenario the inline comment on Lines 173-174 acknowledges but does not isolate.

Wrap the parse in a per-item try/catch so a malformed payload becomes a per-item failure, consistent with how createDashboard failures are already handled.

🐛 Proposed fix to isolate JSON.parse failures
         return Promise.all(
           _.map(selected, (p) => {
-            const board = JSON.parse(p.content);
-            return createDashboard(bgid, { ...board, configs: JSON.stringify(board.configs) }).then((r) => ({ payload: p, err: _.get(r, 'err') as string | undefined }));
+            let board: any;
+            try {
+              board = JSON.parse(p.content);
+            } catch (e) {
+              return Promise.resolve({ payload: p, err: e instanceof Error ? e.message : String(e) });
+            }
+            return createDashboard(bgid, { ...board, configs: JSON.stringify(board.configs) }).then((r) => ({ payload: p, err: _.get(r, 'err') as string | undefined }));
           }),
         ).then((results) => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/TemplateMatch/ImportModal.tsx` around lines 121 - 180, Update
importDashboards around the selected-item mapping so JSON.parse failures are
caught per dashboard instead of escaping the _.map callback. Convert malformed
payloads into the same per-item { payload, err } failure shape used for
createDashboard errors, allowing valid dashboards to continue through
Promise.all and appear in the existing failed-dashboard Modal.error reporting.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pages/datasource/utils/useDataProbe.ts`:
- Around line 133-139: Make the staleData metricCount semantics consistent with
the ProbeResult contract: update the branch checking probed.ts === undefined to
return the 24-hour metric count from the established seenMetrics collection,
matching the other staleData return path. Keep freshMetrics.length for states
whose count specifically represents the 5-minute window.

---

Outside diff comments:
In `@src/components/TemplateMatch/ImportModal.tsx`:
- Around line 121-180: Update importDashboards around the selected-item mapping
so JSON.parse failures are caught per dashboard instead of escaping the _.map
callback. Convert malformed payloads into the same per-item { payload, err }
failure shape used for createDashboard errors, allowing valid dashboards to
continue through Promise.all and appear in the existing failed-dashboard
Modal.error reporting.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: aa5d81c4-3d81-497e-92db-23271a8cae2e

📥 Commits

Reviewing files that changed from the base of the PR and between 9b92522 and f50fdff.

📒 Files selected for processing (5)
  • src/components/TemplateMatch/ImportModal.tsx
  • src/pages/datasource/nextActions.test.ts
  • src/pages/datasource/nextActions.ts
  • src/pages/datasource/utils/useDataProbe.test.ts
  • src/pages/datasource/utils/useDataProbe.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/pages/datasource/nextActions.test.ts
  • src/pages/datasource/nextActions.ts

Comment on lines +133 to +139
// 元数据可读但查询失败:按不可达处理并透出错误
return { state: 'unreachable', metricCount: freshMetrics.length, errorMessage: probed.errorMessage, latencyMs: Date.now() - t0 };
}
if (probed.ts === undefined) {
// 候选指标全都没有近期样本 → 已断流;指标名还在说明 24h 内有过数据,不必再查一次
return { state: 'staleData', metricCount: freshMetrics.length, sampleMetric: pickSampleMetric(freshMetrics), latencyMs: Date.now() - t0 };
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

metricCount semantics diverge from the documented contract for this staleData branch.

The ProbeResult.metricCount doc at line 18 states that for staleData the count reflects the 24-hour window. In this branch, metricCount is set to freshMetrics.length, which comes from the 5-minute window (FRESH_WINDOW_SECONDS), not the 24-hour window. The other staleData branch (lines 120-126) correctly uses seenMetrics.length from the 24-hour window. Consumers of metricCount (for example, UI text rendering "N metrics over the last 24 hours") can receive an inconsistent number depending on which staleData path produced the result.

Clarify the doc comment to describe both staleData cases accurately, or make the count semantics consistent across both staleData return sites.

📝 Proposed doc clarification
-  /** 探测窗口内有数据的指标名数量(hasData 时为近 5 分钟,staleData 时为近 24 小时) */
+  /**
+   * 探测窗口内有数据的指标名数量。
+   * hasData:近 5 分钟窗口内的指标名数。
+   * staleData:若近 5 分钟窗口本身有指标名但未查到新样本,取的是该 5 分钟窗口的指标名数;
+   * 若近 5 分钟窗口为空,取的是回退的 24 小时窗口指标名数。
+   */
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/datasource/utils/useDataProbe.ts` around lines 133 - 139, Make the
staleData metricCount semantics consistent with the ProbeResult contract: update
the branch checking probed.ts === undefined to return the 24-hour metric count
from the established seenMetrics collection, matching the other staleData return
path. Keep freshMetrics.length for states whose count specifically represents
the 5-minute window.

ProbeBanner 渲染在 NextStepModal「探索这些数据」用 target=_blank 开出的新标签页里,
而 target=_blank 隐含 noopener,新顶层上下文没有 creator,浏览器不会把 session storage
复制过去 —— readProbeResult 恒为 undefined,横幅静默渲染成 null,连带 A1.6 落地横幅、
横幅里的模板导入入口、以及 explored_at 旅途标记一起失效,且全程没有任何报错。

改用 localStorage:同源跨标签页共享,与有没有 opener 无关。只落 hasData 一种状态 ——
读取端本来也只认它,而 unreachable 的 errorMessage 可能带着数据源地址,没必要长期
留在本地。残留无需清理:横幅的显隐门是 URL 上的 __from=ds_verify,该标记只由
NextStepModal 在刚跑完体检后产生。

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/pages/datasource/utils/useDataProbe.ts (1)

172-179: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Invalidate an active probe before returning for invalid inputs.

If datasourceId or pluginType becomes invalid while a probe is pending, line 173 returns without changing seq.current. The old probe can then commit its result after the hook is disabled.

Proposed fix
 const run = useCallback(() => {
+  const mySeq = ++seq.current;
   if (!datasourceId || !pluginType) return;
   if (pluginType !== 'prometheus') {
     setProbe({ state: 'unsupported' });
     return;
   }
-  const mySeq = ++seq.current;
   setProbe({ state: 'probing' });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/datasource/utils/useDataProbe.ts` around lines 172 - 179, Update
the run callback in useDataProbe so invalid datasourceId or pluginType inputs
increment seq.current before returning, invalidating any pending probe. Preserve
the existing unsupported-plugin handling and probing sequence behavior for valid
inputs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/pages/datasource/utils/useDataProbe.ts`:
- Around line 172-179: Update the run callback in useDataProbe so invalid
datasourceId or pluginType inputs increment seq.current before returning,
invalidating any pending probe. Preserve the existing unsupported-plugin
handling and probing sequence behavior for valid inputs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b94e43a2-6b1a-46d9-8205-0f7524ec9f0b

📥 Commits

Reviewing files that changed from the base of the PR and between f50fdff and 3a31bee.

📒 Files selected for processing (2)
  • src/pages/datasource/utils/useDataProbe.ts
  • src/pages/explorer/components/ProbeBanner/index.tsx

710leo added 3 commits August 4, 2026 11:08
冲突解决说明:
- OnboardingProgress:main 把步骤点击重构成 useOnboardingStepClick,本分支的
  「不再显示」叠加在其之上;短路标记沿用 main 的 v2 key,存储统一为 localStorage
  (读/写两处保持一致),dismiss 改走 publish 广播让多个挂载点一起收起。
- datasource 列表页:保留 main 的 isAdmin 门禁与 Grafana 导入入口,同时保留本分支
  从保存结果弹窗带回来的 openAddModal 意图(仅对 admin 生效)。
- TableSource 行操作:体检(只读)对所有人可见,编辑 / 删除按 main 的口径仅 admin 可见。
引导清单里的 datasource 一步读的是 CommonStateContext 的数据源列表,不来自探测结论,
DONE_DETECT 把探测项全部置真也补不上它。结果是还没配数据源的用户 —— 恰好是看得见这个
徽标的人 —— 点完「不再显示」只到 8/9,徽标继续挂着,刷新后还会走短路分支永远停在 8/9。

DetectState 增加显式的 dismissed 短路位:DONE_DETECT 带上它,doneMap 的 datasource 与之
取或,进度自然满格,各挂载点统一收起;真实探测回来时沿用原值,不把已关闭的引导冲开。

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/OnboardingProgress/useOnboardingProgress.ts`:
- Line 88: Scope the onboarding completion marker to the authenticated user by
deriving ONBOARDING_DONE_KEY from profile.id. In
src/components/OnboardingProgress/useOnboardingProgress.ts lines 333-337, read
the user-scoped key only after the profile identifier is available; in lines
352-360 and 386-394, persist dismissal and automatic completion using that same
key.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e1552583-ac48-43a6-9a00-0dd06ab7b26a

📥 Commits

Reviewing files that changed from the base of the PR and between 3a31bee and e593a6b.

📒 Files selected for processing (19)
  • src/components/OnboardingProgress/PopoverContent.tsx
  • src/components/OnboardingProgress/index.tsx
  • src/components/OnboardingProgress/useOnboardingProgress.ts
  • src/pages/builtInComponents/AlertRules/Import.tsx
  • src/pages/builtInComponents/AlertRules/ImportForm.tsx
  • src/pages/builtInComponents/AlertRules/index.tsx
  • src/pages/dashboard/List/index.tsx
  • src/pages/datasource/components/TableSource/index.tsx
  • src/pages/datasource/index.tsx
  • src/pages/datasource/locale/en_US.ts
  • src/pages/datasource/locale/ja_JP.ts
  • src/pages/datasource/locale/ru_RU.ts
  • src/pages/datasource/locale/zh_CN.ts
  • src/pages/datasource/locale/zh_HK.ts
  • src/pages/landing/locale/en_US.ts
  • src/pages/landing/locale/ja_JP.ts
  • src/pages/landing/locale/ru_RU.ts
  • src/pages/landing/locale/zh_CN.ts
  • src/pages/landing/locale/zh_HK.ts
🚧 Files skipped from review as they are similar to previous changes (16)
  • src/pages/landing/locale/ja_JP.ts
  • src/pages/landing/locale/zh_HK.ts
  • src/pages/landing/locale/ru_RU.ts
  • src/pages/landing/locale/en_US.ts
  • src/pages/dashboard/List/index.tsx
  • src/pages/datasource/index.tsx
  • src/pages/datasource/locale/ru_RU.ts
  • src/pages/datasource/components/TableSource/index.tsx
  • src/pages/landing/locale/zh_CN.ts
  • src/pages/datasource/locale/zh_CN.ts
  • src/pages/builtInComponents/AlertRules/Import.tsx
  • src/components/OnboardingProgress/index.tsx
  • src/pages/datasource/locale/en_US.ts
  • src/pages/datasource/locale/ja_JP.ts
  • src/pages/datasource/locale/zh_HK.ts
  • src/pages/builtInComponents/AlertRules/ImportForm.tsx

// 全部完成或用户显式关闭后写入持久化标记,后续直接短路、不再探测,避免每次加载都拉全量大盘 / 告警。
// 用 localStorage 而非 sessionStorage:老手关闭一次即永久生效,不随会话结束复活。
// key 带版本号:步骤集合变化后 total 也变了,沿用旧 key 会把老用户永久钉在「已完成」、再也看不到新步骤。
const ONBOARDING_DONE_KEY = 'n9e_onboarding_done_v2';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Scope the completion marker to the authenticated user.

ONBOARDING_DONE_KEY is shared by every account in the browser. If one account dismisses or completes onboarding, another account immediately receives DONE_DETECT and never probes its own progress.

  • src/components/OnboardingProgress/useOnboardingProgress.ts#L88-L88: derive the marker key from profile.id.
  • src/components/OnboardingProgress/useOnboardingProgress.ts#L333-L337: read the user-scoped key only after the profile identifier is available.
  • src/components/OnboardingProgress/useOnboardingProgress.ts#L352-L360: persist dismissal with the same user-scoped key.
  • src/components/OnboardingProgress/useOnboardingProgress.ts#L386-L394: persist automatic completion with the same user-scoped key.
📍 Affects 1 file
  • src/components/OnboardingProgress/useOnboardingProgress.ts#L88-L88 (this comment)
  • src/components/OnboardingProgress/useOnboardingProgress.ts#L333-L337
  • src/components/OnboardingProgress/useOnboardingProgress.ts#L352-L360
  • src/components/OnboardingProgress/useOnboardingProgress.ts#L386-L394
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/OnboardingProgress/useOnboardingProgress.ts` at line 88, Scope
the onboarding completion marker to the authenticated user by deriving
ONBOARDING_DONE_KEY from profile.id. In
src/components/OnboardingProgress/useOnboardingProgress.ts lines 333-337, read
the user-scoped key only after the profile identifier is available; in lines
352-360 and 386-394, persist dismissal and automatic completion using that same
key.

ModalHOC 用 createRoot 在 body 上另起一棵树,只包了 ConfigProvider + Router,
没有 CommonStateContext.Provider,树里所有 useContext(CommonStateContext) 拿到的
都是 createContext 的默认空对象。ImportForm 新加的通知规则选择器整个落在这棵树里,
于是:

- QuickCreateModal 的 canReadChannels / canCreateChannels 恒为 false。粘 FlashDuty
  集成链接必然报「缺少 FlashDuty 通知媒介,且当前用户无通知媒介创建权限」,管理员
  也一样;IM 媒介被删/停用时同样误报。
- Attributes/TagItem 的 busiGroups / datasourceList 恒为 undefined,新建通知规则时
  「适用范围」里选业务组/数据源的下拉是空的。
- ChannelSelect / TemplateSelect / EventPipelineConfigs 的 isAuthorized 恒为 false。

给 ModalHOC 加可选的 commonState:传了才包一层 Provider,其余 57 个 ModalHOC
调用点行为不变。集成中心的告警规则导入两个入口把 useContext 的值原样传进去。

notificationRulesAuthorized 这个必填 prop 保留(TemplateMatch/ImportModal 那个宿主
也在用,类型兜底有价值),但它原来的注释说的「只能由调用方算好传入」已不成立,
一并改成显式契约的说法。

数据源引导那条路径(NextStepModal / ProbeBanner 里的模板匹配弹窗)在 React 树内,
本来就没这个问题。builtInComponents/Dashboards/Import 子树无 context 消费者,未改。

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/pages/builtInComponents/AlertRules/ImportForm.tsx (1)

176-195: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Prevent duplicate alert-rule imports during pending requests.

The submit button only checks submitDisabled and the Form.Item, not an in-flight request. A second click while createRule is pending can send another POST to /api/n9e/busi-group/${id}/alert-rules/import, which may duplicate rules if the API is not idempotent. Track submission state, show the button loading state, use pendingSubmit to disable the submit button, and release the lock in finally.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/builtInComponents/AlertRules/ImportForm.tsx` around lines 176 -
195, Update the import submission flow around createRule to track an in-flight
request with pendingSubmit: set it before starting the request, disable the
submit button and show its loading state while true, and release it in finally
so the lock clears on both success and failure. Preserve the existing success,
error, and onSuccess behavior.
🧹 Nitpick comments (2)
src/pages/builtInComponents/AlertRules/ImportForm.tsx (1)

239-241: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use Tailwind for the hidden wrapper.

This inline style controls the display property. Use className={contextBound ? 'hidden' : undefined} instead. The wrapper remains mounted, so the current Form.List submission behavior is preserved.

As per coding guidelines: use Tailwind utility classes for component-specific display styles and avoid parallel styling mechanisms.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/builtInComponents/AlertRules/ImportForm.tsx` around lines 239 -
241, Update the wrapper around DatasourceValueSelectV2 to replace its inline
display style with className={contextBound ? 'hidden' : undefined}. Keep the
wrapper mounted and preserve the existing Form.List submission behavior.

Source: Coding guidelines

src/components/ModalHOC.tsx (1)

45-45: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace the changed any types with explicit TypeScript contracts.

These boundaries bypass type checking for modal props, selector data, callbacks, and imported rule records.

  • src/components/ModalHOC.tsx#L45-L45: type render as T & ModalWrapProps or a dedicated internal type.
  • src/pages/builtInComponents/AlertRules/ImportForm.tsx#L35-L42: use explicit business-group, datasource-list, reload-function, and datasource-category types.
  • src/pages/builtInComponents/AlertRules/ImportForm.tsx#L154-L159: parse into unknown, validate the JSON shape, and map typed rule records before calling createRule.

As per coding guidelines: use explicit TypeScript interfaces for Props and avoid any.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ModalHOC.tsx` at line 45, Replace the any-based boundaries
with explicit TypeScript contracts: in src/components/ModalHOC.tsx lines 45-45,
type render using T & ModalWrapProps or a dedicated internal interface; in
src/pages/builtInComponents/AlertRules/ImportForm.tsx lines 35-42, define
explicit business-group, datasource-list, reload-function, and
datasource-category types; and in lines 154-159, parse imported JSON as unknown,
validate its shape, then map typed rule records before calling createRule.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/pages/builtInComponents/AlertRules/ImportForm.tsx`:
- Around line 176-195: Update the import submission flow around createRule to
track an in-flight request with pendingSubmit: set it before starting the
request, disable the submit button and show its loading state while true, and
release it in finally so the lock clears on both success and failure. Preserve
the existing success, error, and onSuccess behavior.

---

Nitpick comments:
In `@src/components/ModalHOC.tsx`:
- Line 45: Replace the any-based boundaries with explicit TypeScript contracts:
in src/components/ModalHOC.tsx lines 45-45, type render using T & ModalWrapProps
or a dedicated internal interface; in
src/pages/builtInComponents/AlertRules/ImportForm.tsx lines 35-42, define
explicit business-group, datasource-list, reload-function, and
datasource-category types; and in lines 154-159, parse imported JSON as unknown,
validate its shape, then map typed rule records before calling createRule.

In `@src/pages/builtInComponents/AlertRules/ImportForm.tsx`:
- Around line 239-241: Update the wrapper around DatasourceValueSelectV2 to
replace its inline display style with className={contextBound ? 'hidden' :
undefined}. Keep the wrapper mounted and preserve the existing Form.List
submission behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 962eccab-7e1c-469c-a1cd-781b0660b382

📥 Commits

Reviewing files that changed from the base of the PR and between e593a6b and c1ba171.

📒 Files selected for processing (3)
  • src/components/ModalHOC.tsx
  • src/pages/builtInComponents/AlertRules/ImportForm.tsx
  • src/pages/builtInComponents/AlertRules/index.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/pages/builtInComponents/AlertRules/index.tsx

@jsers
jsers merged commit 6ea38cd into main Aug 4, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants