feat(datasource): optimize datasource page - #2231
Conversation
保存数据源不再直接跳回列表,改为弹出引导弹窗,串起「数据体检 → 下一步动作 → 组件模板一键导入」。 - 新增数据体检 useDataProbe:Prometheus 系数据源在保存后与列表「数据状态」入口实测连通性、 指标数与最近样本时间,区分 hasData / staleData / noData / unreachable / unsupported 五态 - 新增组件模板匹配面板 TemplateMatch:按后端哨兵指标法的匹配结果展示组件卡片, 就地勾选导入仪表盘与告警规则,导入时自动预绑定当前数据源 - 抽出 nextActions 作为「下一步动作」的单一事实源,保存弹窗、详情抽屉、列表行共用, 能力判断由 Cate 清单驱动,不可用的动作置灰并说明原因而非静默隐藏 - 抽出 builtInComponents/AlertRules/ImportForm,集成中心弹窗与模板匹配内联复用同一份导入逻辑, 顺带支持导入时就地选择通知规则 - 探索器新增体检落地横幅 ProbeBanner,仪表盘/告警规则列表新增引导承接横幅 GuideLandingBanner - 数据源列表行内操作补充「数据状态」与「编辑」,停用开关加二次确认 - 修复探索器 history.replace 拼 search 时漏了 data_source_id 参数名 - 新手引导支持「不再显示」,完成标记从 sessionStorage 改为 localStorage
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughChangesDatasource and onboarding experience
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
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 14
🧹 Nitpick comments (5)
src/pages/builtInComponents/AlertRules/ImportForm.tsx (1)
38-41: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueType the context-derived props.
busiGroups,groupedDatasourceList,reloadGroupedDatasourceList, anddatasourceCateOptionsare typedany. This file is a new shared contract used by two hosts, so the loss of type safety now spreads. Reuse the types exported byCommonStateContext.As per coding guidelines: "Declare component Props explicitly with
interfaceand avoidany."🤖 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 valueConsider hiding the banner when the datasource changes.
probeBannerVisiblestaystruewhen the user selects another datasource in the explorer.ProbeBannerthen reads the probe result of the new datasource and can show a success banner that the user did not ask for, and it also recordsexplored_atfor 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 valueUse 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 valueConsider 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 valueReplace the hardcoded rgba fallback with a theme value.
The class contains
rgba(0,0,0,0.09)as the--fc-shadow-mdfallback. 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.cssand 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
📒 Files selected for processing (38)
src/components/OnboardingProgress/PopoverContent.tsxsrc/components/OnboardingProgress/index.tsxsrc/components/OnboardingProgress/useOnboardingProgress.tssrc/components/PromGraphCpt/index.tsxsrc/components/TemplateMatch/ImportModal.tsxsrc/components/TemplateMatch/index.tsxsrc/components/TemplateMatch/services.tssrc/pages/alertRules/List/index.tsxsrc/pages/builtInComponents/AlertRules/Import.tsxsrc/pages/builtInComponents/AlertRules/ImportForm.tsxsrc/pages/builtInComponents/Dashboards/Import.tsxsrc/pages/dashboard/List/index.tsxsrc/pages/datasource/Detail.tsxsrc/pages/datasource/Form.tsxsrc/pages/datasource/components/GuideLandingBanner.tsxsrc/pages/datasource/components/NextActionButton.tsxsrc/pages/datasource/components/NextStepModal.tsxsrc/pages/datasource/components/TableSource/index.tsxsrc/pages/datasource/index.lesssrc/pages/datasource/index.tsxsrc/pages/datasource/locale/en_US.tssrc/pages/datasource/locale/ja_JP.tssrc/pages/datasource/locale/ru_RU.tssrc/pages/datasource/locale/zh_CN.tssrc/pages/datasource/locale/zh_HK.tssrc/pages/datasource/nextActions.test.tssrc/pages/datasource/nextActions.tssrc/pages/datasource/utils/journey.tssrc/pages/datasource/utils/useDataProbe.test.tssrc/pages/datasource/utils/useDataProbe.tssrc/pages/explorer/Explorer.tsxsrc/pages/explorer/Prometheus/index.tsxsrc/pages/explorer/components/ProbeBanner/index.tsxsrc/pages/landing/locale/en_US.tssrc/pages/landing/locale/ja_JP.tssrc/pages/landing/locale/ru_RU.tssrc/pages/landing/locale/zh_CN.tssrc/pages/landing/locale/zh_HK.ts
💤 Files with no reviewable changes (1)
- src/pages/datasource/index.less
| {onDismiss && ( | ||
| <div className='text-right mt-1'> | ||
| <a className='text-[var(--fc-text-4)] text-xs' onClick={onDismiss}> | ||
| {t('onboarding.dismiss')} | ||
| </a> | ||
| </div> | ||
| )} |
There was a problem hiding this comment.
🎯 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.
| {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.
| if (localStorage.getItem(ONBOARDING_DONE_KEY)) { | ||
| setDetect(DONE_DETECT); | ||
| return; | ||
| } |
There was a problem hiding this comment.
🩺 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 200Repository: 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:
- 1: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
- 2: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage?swcfpc=1
- 3: https://html.spec.whatwg.org/multipage/webstorage.html
- 4: https://www.chromium.org/for-testers/bug-reporting-guidelines/uncaught-securityerror-failed-to-read-the-localstorage-property-from-window-access-is-denied-for-this-document/
- 5: https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API
- 6: https://stackoverflow.com/questions/70811129/securityerror-failed-to-read-the-localstorage-property-from-window-access
- 7: https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API
- 8: https://stackoverflow.com/questions/73052049/how-to-handle-errors-when-localstorage-permission-is-not-granted
- 9: https://stackoverflow.com/questions/16427636/check-if-localstorage-is-available
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.
| 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); | ||
| }); | ||
| }; |
There was a problem hiding this comment.
🗄️ 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.
| <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} |
There was a problem hiding this comment.
🎯 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.
| <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
| 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]); |
There was a problem hiding this comment.
📐 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.
| 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
| {result && ( | ||
| <NextStepModal | ||
| datasourceId={result.id} | ||
| pluginType={type} | ||
| name={result.name} | ||
| mode={action === 'edit' ? 'updated' : 'saved'} | ||
| verification={result.verification} | ||
| onClose={() => { |
There was a problem hiding this comment.
🎯 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.
| {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.
| 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: '連通性與數據情況請到探索器裡實際查一次。', |
There was a problem hiding this comment.
📐 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:
暂不支持→暫不支援(seeauth.not-supportat 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.
| <Link to='/dashboards' target='_blank'> | ||
| {t('result.create_dashboard')} | ||
| </Link> | ||
| <Link to='/alert-rules' target='_blank'> | ||
| {t('result.create_alert')} | ||
| </Link> |
There was a problem hiding this comment.
🎯 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/explorerRepository: 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
doneRepository: 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 || trueRepository: 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.tsxRepository: 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.
There was a problem hiding this comment.
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 winIsolate
JSON.parsefailures per dashboard to preserve partial-import success.
createDashboard(insrc/pages/builtInComponents/Dashboards/services.ts) catches its own errors and resolves{ err }rather than rejecting, so wrapping calls inPromise.allno 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, beforePromise.allstarts any request. If one selected payload has malformed JSON,_.mapthrows synchronously, and the whole call aborts before anycreateDashboardrequest fires. None of the selected dashboards get created, not just the one with bad content. The user only sees the generictpl_match.import_errormessage from the outer.catchat 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/catchso a malformed payload becomes a per-item failure, consistent with howcreateDashboardfailures 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
📒 Files selected for processing (5)
src/components/TemplateMatch/ImportModal.tsxsrc/pages/datasource/nextActions.test.tssrc/pages/datasource/nextActions.tssrc/pages/datasource/utils/useDataProbe.test.tssrc/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
| // 元数据可读但查询失败:按不可达处理并透出错误 | ||
| 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 }; | ||
| } |
There was a problem hiding this comment.
🎯 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 在刚跑完体检后产生。
There was a problem hiding this comment.
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 winInvalidate an active probe before returning for invalid inputs.
If
datasourceIdorpluginTypebecomes invalid while a probe is pending, line 173 returns without changingseq.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
📒 Files selected for processing (2)
src/pages/datasource/utils/useDataProbe.tssrc/pages/explorer/components/ProbeBanner/index.tsx
冲突解决说明: - 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 与之 取或,进度自然满格,各挂载点统一收起;真实探测回来时沿用原值,不把已关闭的引导冲开。
…etch without permission
There was a problem hiding this comment.
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
📒 Files selected for processing (19)
src/components/OnboardingProgress/PopoverContent.tsxsrc/components/OnboardingProgress/index.tsxsrc/components/OnboardingProgress/useOnboardingProgress.tssrc/pages/builtInComponents/AlertRules/Import.tsxsrc/pages/builtInComponents/AlertRules/ImportForm.tsxsrc/pages/builtInComponents/AlertRules/index.tsxsrc/pages/dashboard/List/index.tsxsrc/pages/datasource/components/TableSource/index.tsxsrc/pages/datasource/index.tsxsrc/pages/datasource/locale/en_US.tssrc/pages/datasource/locale/ja_JP.tssrc/pages/datasource/locale/ru_RU.tssrc/pages/datasource/locale/zh_CN.tssrc/pages/datasource/locale/zh_HK.tssrc/pages/landing/locale/en_US.tssrc/pages/landing/locale/ja_JP.tssrc/pages/landing/locale/ru_RU.tssrc/pages/landing/locale/zh_CN.tssrc/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'; |
There was a problem hiding this comment.
🎯 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 fromprofile.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-L337src/components/OnboardingProgress/useOnboardingProgress.ts#L352-L360src/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 消费者,未改。
There was a problem hiding this comment.
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 winPrevent duplicate alert-rule imports during pending requests.
The submit button only checks
submitDisabledand theForm.Item, not an in-flight request. A second click whilecreateRuleis 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, usependingSubmitto disable the submit button, and release the lock infinally.🤖 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 winUse Tailwind for the hidden wrapper.
This inline style controls the
displayproperty. UseclassName={contextBound ? 'hidden' : undefined}instead. The wrapper remains mounted, so the currentForm.Listsubmission 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 winReplace the changed
anytypes 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: typerenderasT & ModalWrapPropsor 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 intounknown, validate the JSON shape, and map typed rule records before callingcreateRule.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
📒 Files selected for processing (3)
src/components/ModalHOC.tsxsrc/pages/builtInComponents/AlertRules/ImportForm.tsxsrc/pages/builtInComponents/AlertRules/index.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- src/pages/builtInComponents/AlertRules/index.tsx
Summary by CodeRabbit
Summary by CodeRabbit
New Features
Bug Fixes
Localization