From 262823edf89c3b9bd783a10e374680a25bf4b353 Mon Sep 17 00:00:00 2001 From: ironAiken2 <51399982+ironAiken2@users.noreply.github.com> Date: Thu, 23 Apr 2026 07:35:26 +0000 Subject: [PATCH] refactor(FR-2479): standardize confirmation UX with BAIConfirmModalWithInput and Popconfirm (#6471) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves #6437 (FR-2479) ## Summary - **Phase 1**: Upgrade 6 destructive actions from `modal.confirm()` to `BAIConfirmModalWithInput` (typed-input confirmation) - `ResourcePresetList.tsx` — preset deletion - `EndpointList.tsx` — endpoint deletion - `ShellScriptEditModal.tsx` — script deletion - `AIAgentPage.tsx` — agent deletion and reset - `ManageAppsModal.tsx` — image reinstallation confirmation - `ManageImageResourceLimitModal.tsx` — image reinstallation confirmation - **Phase 2**: Convert 2 TOTP toggle confirmations from `modal.confirm()` to `Popconfirm` - `UserSettingModal.tsx` — TOTP deactivation toggle - `UserProfileSettingModal.tsx` — TOTP deactivation toggle - **Phase 3**: Remove orphaned `PopConfirmWithInput.tsx` (0 imports) ## Test plan - [ ] Delete a resource preset — typed confirmation modal appears, must type preset name - [ ] Delete an endpoint — typed confirmation modal appears, must type endpoint name - [ ] Delete a shell script — typed confirmation modal appears - [ ] Delete/reset an AI agent — typed confirmation modal appears - [ ] Modify apps on installed image — typed confirmation modal appears - [ ] Modify resource limits on installed image — typed confirmation modal appears - [ ] Toggle TOTP off (UserSettingModal) — Popconfirm appears inline - [ ] Toggle TOTP off (UserProfileSettingModal) — Popconfirm appears inline - [ ] Verify `PopConfirmWithInput.tsx` is removed --- .claude/rules/destructive-confirmation.md | 95 ++++++++++++++ packages/backend.ai-ui/src/locale/ko.json | 2 +- react/src/components/EndpointList.tsx | 119 +++++++++-------- react/src/components/ManageAppsModal.tsx | 51 ++++--- .../ManageImageResourceLimitModal.tsx | 50 +++---- react/src/components/PopConfirmWithInput.tsx | 112 ---------------- react/src/components/ResourcePresetList.tsx | 124 ++++++++++-------- react/src/components/ShellScriptEditModal.tsx | 108 ++++++++++----- react/src/components/VFolderNodes.tsx | 1 + react/src/pages/AIAgentPage.tsx | 90 ++++++++++--- resources/i18n/de.json | 1 + resources/i18n/el.json | 1 + resources/i18n/en.json | 1 + resources/i18n/es.json | 1 + resources/i18n/fi.json | 1 + resources/i18n/fr.json | 1 + resources/i18n/id.json | 1 + resources/i18n/it.json | 1 + resources/i18n/ja.json | 1 + resources/i18n/ko.json | 3 +- resources/i18n/mn.json | 1 + resources/i18n/ms.json | 1 + resources/i18n/pl.json | 1 + resources/i18n/pt-BR.json | 1 + resources/i18n/pt.json | 1 + resources/i18n/ru.json | 1 + resources/i18n/th.json | 1 + resources/i18n/tr.json | 1 + resources/i18n/vi.json | 1 + resources/i18n/zh-CN.json | 1 + resources/i18n/zh-TW.json | 1 + 31 files changed, 459 insertions(+), 316 deletions(-) create mode 100644 .claude/rules/destructive-confirmation.md delete mode 100644 react/src/components/PopConfirmWithInput.tsx diff --git a/.claude/rules/destructive-confirmation.md b/.claude/rules/destructive-confirmation.md new file mode 100644 index 0000000000..cfaafa44d8 --- /dev/null +++ b/.claude/rules/destructive-confirmation.md @@ -0,0 +1,95 @@ +# Destructive Action Confirmation Rule + +For **irreversible destructive actions** (permanent deletion, purge, force termination, data wipe), confirmation must be collected through a **modal that requires the user to type a confirmation string** — not a `Popconfirm`, not a plain `Modal.confirm`, not a single-click dialog. + +## Why + +Popconfirm and one-click confirmation dialogs are appropriate for **reversible or low-impact** actions (inactivating, hiding, unassigning, canceling a draft). For actions the user cannot undo, a single misclick has permanent consequences. Requiring the user to type a specific string (typically the resource's name) forces a deliberate pause and prevents accidental destruction. + +This convention was applied project-wide in FR-2479 ("standardize confirmation UX"), which replaced the legacy `PopConfirmWithInput.tsx` with the shared `BAIConfirmModalWithInput` component. + +## Rules + +1. **Irreversible actions → `BAIConfirmModalWithInput`** (from `backend.ai-ui`). The user must type a confirmation string (usually the resource's name) before the OK button enables. Examples: permanently delete a VFolder, terminate a model service endpoint, purge a user, delete an image, delete a resource preset, remove a shell script. +2. **Reversible / low-impact actions → `Popconfirm`** or `App.useApp().modal.confirm({ ... })`. Examples: deactivating (not deleting) a user, canceling an in-progress action, hiding an item, marking inactive. +3. **Never use `Popconfirm` for permanent deletion**, even when the action is guarded server-side. The UX contract is about *user intent*, not backend safety. +4. Do **not** reintroduce `PopConfirmWithInput` or any ad-hoc "modal with a text input" for destructive flows — use the shared `BAIConfirmModalWithInput`. This keeps the copy, layout, danger styling, and accessibility consistent. +5. The confirmation string should be something the user sees on screen and can copy unambiguously (e.g., the resource's `name` or `id`). Avoid opaque tokens. + +## Pattern + +### ❌ Wrong — Popconfirm for permanent deletion + +```tsx + deleteForever(row.id)} +> + - { - modal.confirm({ - title: t('dialog.title.LetsDouble-Check'), - content: t('dialog.ask.DoYouWantToResetChanges'), - onOk: () => { - setScript(''); - }, - }); - }, - danger: true, - }, - ], + { + setScript(''); + setIsResetConfirmOpen(false); }} + onCancel={() => setIsResetConfirmOpen(false)} > -