react-bootstrap-plugins 2.5.1 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,114 @@
1
+ # FormRow
2
+
3
+ > A Bootstrap-styled form row wrapper — label, control, helper text, and required indicator in one block.
4
+
5
+ ---
6
+
7
+ ## Import
8
+
9
+ ```js
10
+ // Default import (recommended for single-component tree-shaking)
11
+ import FormRow from 'react-bootstrap-plugins/FormRow'
12
+
13
+ // Named import from individual entry point
14
+ import { FormRow } from 'react-bootstrap-plugins/FormRow'
15
+
16
+ // Barrel import
17
+ import { FormRow } from 'react-bootstrap-plugins'
18
+ ```
19
+
20
+ ---
21
+
22
+ ## Basic Usage
23
+
24
+ ```jsx
25
+ import { FormRow } from 'react-bootstrap-plugins'
26
+
27
+ <FormRow label="Student Name" required hint="Full name as it appears on the birth certificate">
28
+ <input type="text" className="form-control" />
29
+ </FormRow>
30
+ ```
31
+
32
+ Renders:
33
+
34
+ ```html
35
+ <div>
36
+ <label class="form-label small fw-medium">Student Name <b class="text-danger ms-1">*</b></label>
37
+ <input type="text" class="form-control" />
38
+ <div class="form-text">Full name as it appears on the birth certificate</div>
39
+ </div>
40
+ ```
41
+
42
+ ---
43
+
44
+ ## Props
45
+
46
+ | Prop | Type | Default | Description |
47
+ |---|---|---|---|
48
+ | `label` | `string` | — | Label text rendered above the field |
49
+ | `hint` | `string` | — | Helper text rendered below the field (`form-text`) |
50
+ | `required` | `boolean` | `false` | Show red asterisk after the label |
51
+ | `className` | `string` | — | Additional CSS classes on the wrapper `<div>` |
52
+ | `children` | `ReactNode` | — | Form control(s) rendered inside the row |
53
+
54
+ `FormRow` also forwards its `ref` to the wrapper `<div>`.
55
+
56
+ ---
57
+
58
+ ## Examples
59
+
60
+ ### Required field with hint
61
+
62
+ ```jsx
63
+ <FormRow label="Email Address" required hint="We'll never share your email">
64
+ <input type="email" className="form-control" placeholder="you@school.ac.ug" />
65
+ </FormRow>
66
+ ```
67
+
68
+ ### Optional field (no asterisk)
69
+
70
+ ```jsx
71
+ <FormRow label="Middle Name">
72
+ <input type="text" className="form-control" />
73
+ </FormRow>
74
+ ```
75
+
76
+ ### With a Select control
77
+
78
+ ```jsx
79
+ <FormRow label="Class" required>
80
+ <select className="form-select">
81
+ <option value="">Choose a class…</option>
82
+ <option value="s1">Senior One</option>
83
+ <option value="s2">Senior Two</option>
84
+ </select>
85
+ </FormRow>
86
+ ```
87
+
88
+ ### With extra wrapper classes
89
+
90
+ ```jsx
91
+ <FormRow label="Phone" className="col-md-6 mb-3">
92
+ <input type="tel" className="form-control" />
93
+ </FormRow>
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Dark Mode
99
+
100
+ The FormRow respects Bootstrap 5's dark mode. Set `data-bs-theme="dark"` on any parent element.
101
+
102
+ ---
103
+
104
+ ## Bundle Size
105
+
106
+ ~0.3 KB (min+gzip).
107
+
108
+ ---
109
+
110
+ ## See Also
111
+
112
+ - [Label](./LABEL.md) — Standalone form label with required indicator
113
+ - [InputCurrency](./INPUTCURRENCY.md) — Controlled currency input
114
+ - [Main README](../README.md) — Package overview, installation, and general info
@@ -0,0 +1,549 @@
1
+ # ConfirmDialog Plugin Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Add a premium zero-dependency `ConfirmDialog` component to react-bootstrap-plugins and migrate the portal's local ConfirmDialog in CBCAlevelReviewResults.jsx to it.
6
+
7
+ **Architecture:** Bootstrap 5 modal markup rendered via `createPortal` to `document.body` — backdrop div + `.modal > .modal-dialog.modal-dialog-centered.modal-sm > .modal-content`. No react-bootstrap, no animation, fully controlled via `show`. "Refined Minimal" premium styling appended to `src/css/plugins.css`. Async `onConfirm` drives a pending spinner state.
8
+
9
+ **Tech Stack:** TypeScript + React 18 (forwardRef, hooks), Bootstrap 5 CSS (classes + CSS variables), tsup build, pnpm.
10
+
11
+ **Testing note (documented deviation, spec-approved):** the package has no test runner (`"test": "echo 'Tests passed'"`). Verification = `pnpm run build` (tsup + dts + CSS) after each task, plus a live smoke checklist in the portal at the end. No vitest is added.
12
+
13
+ **Spec:** `docs/superpowers/specs/2026-08-19-confirmdialog-plugin-design.md`
14
+
15
+ ---
16
+
17
+ ### Task 1: ConfirmDialog component
18
+
19
+ **Files:**
20
+ - Create: `/media/robert/Development/Allios Apps/allios-global/react-bootstrap-plugins/src/components/ConfirmDialog.tsx`
21
+
22
+ - [ ] **Step 1: Write the component**
23
+
24
+ ```tsx
25
+ import * as React from 'react'
26
+ import { createPortal } from 'react-dom'
27
+ import { cn } from '../lib/cn.js'
28
+
29
+ /* ------------------------------------------------------------------ */
30
+ /* Types */
31
+ /* ------------------------------------------------------------------ */
32
+
33
+ export type ConfirmDialogVariant = 'primary' | 'danger' | 'success' | 'warning' | 'info'
34
+
35
+ export interface ConfirmDialogProps {
36
+ /** Visibility — fully controlled by the caller */
37
+ show: boolean
38
+ /** Dialog title */
39
+ title: React.ReactNode
40
+ /** Body message (string or JSX) */
41
+ message: React.ReactNode
42
+ /**
43
+ * Fired when the confirm button is clicked. May return a promise —
44
+ * the dialog shows a pending spinner until it resolves.
45
+ * On rejection the dialog stays open and the error propagates;
46
+ * callers handle their own error display inside `onConfirm`.
47
+ */
48
+ onConfirm: () => void | Promise<void>
49
+ /** Fired on the Cancel button or the Escape key */
50
+ onCancel: () => void
51
+ /** Confirm button label */
52
+ label?: React.ReactNode
53
+ /** Bootstrap color variant for the confirm button, header dot, and message rule */
54
+ variant?: ConfirmDialogVariant
55
+ /** Extra CSS classes on the dialog element */
56
+ className?: string
57
+ }
58
+
59
+ /* ------------------------------------------------------------------ */
60
+ /* ConfirmDialog */
61
+ /* ------------------------------------------------------------------ */
62
+
63
+ /**
64
+ * Zero-dependency Bootstrap 5 confirmation dialog.
65
+ *
66
+ * Renders standard `.modal` markup portaled to `document.body` with its own
67
+ * backdrop — no react-bootstrap required. Backdrop clicks do nothing;
68
+ * Escape fires `onCancel`; focus is trapped inside the dialog and lands on
69
+ * the confirm button on open. Body scroll is locked while shown.
70
+ *
71
+ * **Important:** the accompanying CSS **must** be imported for the premium
72
+ * styling (backdrop, radius, header dot, message rule):
73
+ * ```js
74
+ * import 'react-bootstrap-plugins/css/plugins.css'
75
+ * ```
76
+ */
77
+ const ConfirmDialog = React.forwardRef<HTMLDivElement, ConfirmDialogProps>(({
78
+ show = false,
79
+ title,
80
+ message,
81
+ onConfirm,
82
+ onCancel,
83
+ label = 'Confirm',
84
+ variant = 'primary',
85
+ className,
86
+ }, ref) => {
87
+ const [isPending, setIsPending] = React.useState(false)
88
+ const titleId = React.useId()
89
+ const messageId = React.useId()
90
+ const dialogRef = React.useRef<HTMLDivElement>(null)
91
+ const confirmRef = React.useRef<HTMLButtonElement>(null)
92
+
93
+ /* Focus the primary action when the dialog opens */
94
+ React.useEffect(() => {
95
+ if (show) confirmRef.current?.focus()
96
+ }, [show])
97
+
98
+ /* Body scroll lock while shown (compensate scrollbar disappearance) */
99
+ React.useEffect(() => {
100
+ if (!show) return
101
+ const { body } = document
102
+ const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth
103
+ const prevOverflow = body.style.overflow
104
+ const prevPaddingRight = body.style.paddingRight
105
+ body.style.overflow = 'hidden'
106
+ if (scrollbarWidth > 0) body.style.paddingRight = `${scrollbarWidth}px`
107
+ return () => {
108
+ body.style.overflow = prevOverflow
109
+ body.style.paddingRight = prevPaddingRight
110
+ }
111
+ }, [show])
112
+
113
+ /* Escape closes (unless a confirm is pending) */
114
+ React.useEffect(() => {
115
+ if (!show) return
116
+ const handleKey = (e: KeyboardEvent) => {
117
+ if (e.key === 'Escape' && !isPending) onCancel()
118
+ }
119
+ document.addEventListener('keydown', handleKey)
120
+ return () => document.removeEventListener('keydown', handleKey)
121
+ }, [show, isPending, onCancel])
122
+
123
+ /* Tab / Shift+Tab cycle within the dialog */
124
+ const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
125
+ if (e.key !== 'Tab') return
126
+ const root = dialogRef.current
127
+ if (!root) return
128
+ const focusables = Array.from(
129
+ root.querySelectorAll<HTMLElement>('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')
130
+ ).filter((el) => !el.hasAttribute('disabled'))
131
+ if (!focusables.length) return
132
+ const first = focusables[0]
133
+ const last = focusables[focusables.length - 1]
134
+ if (e.shiftKey && document.activeElement === first) {
135
+ e.preventDefault()
136
+ last.focus()
137
+ } else if (!e.shiftKey && document.activeElement === last) {
138
+ e.preventDefault()
139
+ first.focus()
140
+ }
141
+ }
142
+
143
+ const handleConfirmClick = async () => {
144
+ if (isPending) return
145
+ setIsPending(true)
146
+ try {
147
+ await onConfirm()
148
+ } finally {
149
+ setIsPending(false)
150
+ }
151
+ }
152
+
153
+ if (!show || typeof document === 'undefined') return null
154
+
155
+ return createPortal(
156
+ <>
157
+ <div className="confirmdialog-backdrop" aria-hidden="true" />
158
+ <div
159
+ ref={ref}
160
+ tabIndex={-1}
161
+ role="dialog"
162
+ aria-modal="true"
163
+ aria-labelledby={titleId}
164
+ aria-describedby={messageId}
165
+ className={cn('modal confirmdialog d-block', className)}
166
+ onKeyDown={handleKeyDown}
167
+ >
168
+ <div ref={dialogRef} className="modal-dialog modal-dialog-centered modal-sm">
169
+ <div className="modal-content">
170
+ <div className="confirmdialog-header">
171
+ <span className={cn('confirmdialog-dot', `bg-${variant}`)} aria-hidden="true" />
172
+ <h5 className="modal-title" id={titleId}>{title}</h5>
173
+ </div>
174
+ <div
175
+ id={messageId}
176
+ className={cn('confirmdialog-body border-start border-3', `border-${variant}`)}
177
+ >
178
+ {message}
179
+ </div>
180
+ <div className="confirmdialog-footer">
181
+ <button type="button" className="btn btn-light" onClick={onCancel} disabled={isPending}>
182
+ Cancel
183
+ </button>
184
+ <button
185
+ ref={confirmRef}
186
+ type="button"
187
+ className={cn('btn', `btn-${variant}`, 'fw-semibold')}
188
+ onClick={handleConfirmClick}
189
+ disabled={isPending}
190
+ >
191
+ {isPending && <span className="spinner-border spinner-border-sm me-2" aria-hidden="true" />}
192
+ {label}
193
+ </button>
194
+ </div>
195
+ </div>
196
+ </div>
197
+ </div>
198
+ </>,
199
+ document.body
200
+ )
201
+ })
202
+
203
+ ConfirmDialog.displayName = 'ConfirmDialog'
204
+
205
+ export { ConfirmDialog }
206
+ export default ConfirmDialog
207
+ ```
208
+
209
+ - [ ] **Step 2: Verify types compile**
210
+
211
+ Run: `cd "/media/robert/Development/Allios Apps/allios-global/react-bootstrap-plugins" && pnpm run build:types`
212
+ Expected: exit 0, `DTS Build success`
213
+
214
+ - [ ] **Step 3: Commit**
215
+
216
+ ```bash
217
+ git add src/components/ConfirmDialog.tsx
218
+ git commit -m "feat: add ConfirmDialog component"
219
+ ```
220
+
221
+ ---
222
+
223
+ ### Task 2: ConfirmDialog styles
224
+
225
+ **Files:**
226
+ - Modify: `/media/robert/Development/Allios Apps/allios-global/react-bootstrap-plugins/src/css/plugins.css` (append at end of file)
227
+
228
+ - [ ] **Step 1: Append the ConfirmDialog CSS section**
229
+
230
+ Append exactly this to the end of `src/css/plugins.css`:
231
+
232
+ ```css
233
+ /* ==========================================================================
234
+ ConfirmDialog — "Refined Minimal" premium confirmation dialog
235
+ Bootstrap 5 modal markup portaled to document.body. Theme-aware via
236
+ --bs-* variables (dark mode works automatically).
237
+ ========================================================================== */
238
+
239
+ .confirmdialog-backdrop {
240
+ position: fixed;
241
+ inset: 0;
242
+ z-index: 1050;
243
+ background: rgba(0, 0, 0, 0.5);
244
+ }
245
+
246
+ .confirmdialog .modal-content {
247
+ border: 1px solid var(--bs-border-color, #dee2e6);
248
+ border-radius: 12px;
249
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
250
+ }
251
+
252
+ .confirmdialog-header {
253
+ display: flex;
254
+ align-items: center;
255
+ gap: 8px;
256
+ padding: 16px 18px 12px;
257
+ }
258
+
259
+ .confirmdialog-dot {
260
+ width: 8px;
261
+ height: 8px;
262
+ border-radius: 50%;
263
+ flex-shrink: 0;
264
+ }
265
+
266
+ .confirmdialog-header .modal-title {
267
+ font-weight: 600;
268
+ }
269
+
270
+ .confirmdialog-body {
271
+ padding: 4px 18px 16px 34px;
272
+ color: var(--bs-secondary-color, #6c757d);
273
+ font-size: 0.875rem;
274
+ line-height: 1.55;
275
+ }
276
+
277
+ .confirmdialog-footer {
278
+ display: flex;
279
+ justify-content: flex-end;
280
+ gap: 8px;
281
+ padding: 12px 18px 16px;
282
+ }
283
+ ```
284
+
285
+ - [ ] **Step 2: Verify CSS builds**
286
+
287
+ Run: `cd "/media/robert/Development/Allios Apps/allios-global/react-bootstrap-plugins" && pnpm run build`
288
+ Expected: exit 0, output includes `✓ plugins.css (minified)` and `CSS built successfully.`
289
+
290
+ - [ ] **Step 3: Commit**
291
+
292
+ ```bash
293
+ git add src/css/plugins.css
294
+ git commit -m "style: add ConfirmDialog premium styles"
295
+ ```
296
+
297
+ ---
298
+
299
+ ### Task 3: Package wiring
300
+
301
+ **Files:**
302
+ - Modify: `/media/robert/Development/Allios Apps/allios-global/react-bootstrap-plugins/tsup.config.ts`
303
+ - Modify: `/media/robert/Development/Allios Apps/allios-global/react-bootstrap-plugins/package.json`
304
+ - Modify: `/media/robert/Development/Allios Apps/allios-global/react-bootstrap-plugins/src/index.ts`
305
+
306
+ - [ ] **Step 1: Add tsup entry**
307
+
308
+ In `tsup.config.ts`, add one line to the `entry` object after the `FormRow` line:
309
+
310
+ ```ts
311
+ ConfirmDialog: 'src/components/ConfirmDialog.tsx',
312
+ ```
313
+
314
+ - [ ] **Step 2: Add exports map entry**
315
+
316
+ In `package.json`, after the `"./FormRow"` block (it ends with `},`), add:
317
+
318
+ ```json
319
+ "./ConfirmDialog": {
320
+ "import": {
321
+ "types": "./dist/ConfirmDialog.d.ts",
322
+ "default": "./dist/ConfirmDialog.js"
323
+ },
324
+ "require": {
325
+ "types": "./dist/ConfirmDialog.d.ts",
326
+ "default": "./dist/ConfirmDialog.cjs"
327
+ }
328
+ },
329
+ ```
330
+
331
+ - [ ] **Step 3: Bump version**
332
+
333
+ In `package.json`, change `"version": "2.6.0"` to `"version": "2.7.0"`.
334
+
335
+ - [ ] **Step 4: Add keywords**
336
+
337
+ In `package.json`, in the `keywords` array, add before `"ui-components"`:
338
+
339
+ ```json
340
+ "confirm-dialog",
341
+ "confirmation",
342
+ "modal",
343
+ ```
344
+
345
+ - [ ] **Step 5: Barrel export**
346
+
347
+ In `src/index.ts`, after the `FormRow` export lines, add:
348
+
349
+ ```ts
350
+ export { ConfirmDialog } from './components/ConfirmDialog.js'
351
+ ```
352
+
353
+ and after the `FormRowProps` type export line:
354
+
355
+ ```ts
356
+ export type { ConfirmDialogProps, ConfirmDialogVariant } from './components/ConfirmDialog.js'
357
+ ```
358
+
359
+ - [ ] **Step 6: Verify full build and dist output**
360
+
361
+ Run: `cd "/media/robert/Development/Allios Apps/allios-global/react-bootstrap-plugins" && pnpm run build && ls dist/ConfirmDialog.*`
362
+ Expected: exit 0, and output lists exactly `ConfirmDialog.cjs`, `ConfirmDialog.d.cts`, `ConfirmDialog.d.mts`, `ConfirmDialog.d.ts`, `ConfirmDialog.js`
363
+
364
+ - [ ] **Step 7: Commit**
365
+
366
+ ```bash
367
+ git add tsup.config.ts package.json src/index.ts
368
+ git commit -m "feat: wire ConfirmDialog into build, exports, and barrel"
369
+ ```
370
+
371
+ ---
372
+
373
+ ### Task 4: Documentation
374
+
375
+ **Files:**
376
+ - Create: `/media/robert/Development/Allios Apps/allios-global/react-bootstrap-plugins/docs/CONFIRMDIALOG.md`
377
+ - Modify: `/media/robert/Development/Allios Apps/allios-global/react-bootstrap-plugins/README.md`
378
+
379
+ - [ ] **Step 1: Write the component guide**
380
+
381
+ Create `docs/CONFIRMDIALOG.md`:
382
+
383
+ ```markdown
384
+ # ConfirmDialog
385
+
386
+ Zero-dependency Bootstrap 5 confirmation dialog with async loading state. Premium "Refined Minimal" styling — soft radius, variant-colored header dot, variant rule on the message.
387
+
388
+ ## Import
389
+
390
+ ```js
391
+ import ConfirmDialog from 'react-bootstrap-plugins/ConfirmDialog'
392
+ // or
393
+ import { ConfirmDialog } from 'react-bootstrap-plugins'
394
+ // Required CSS
395
+ import 'react-bootstrap-plugins/css/plugins.css'
396
+ ```
397
+
398
+ Requires Bootstrap 5 CSS loaded in your app.
399
+
400
+ ## Usage
401
+
402
+ ```jsx
403
+ const [deleteTarget, setDeleteTarget] = useState(null)
404
+
405
+ <ConfirmDialog
406
+ show={!!deleteTarget}
407
+ title="Delete Report Card"
408
+ message="Are you sure you want to delete this report card? This action cannot be undone."
409
+ label="Confirm Delete"
410
+ variant="danger"
411
+ onConfirm={async () => {
412
+ await deleteReportCard(deleteTarget.id) // spinner shows until this settles
413
+ setDeleteTarget(null) // caller controls closing
414
+ }}
415
+ onCancel={() => setDeleteTarget(null)}
416
+ />
417
+ ```
418
+
419
+ The dialog is fully controlled: it never closes itself. Close by setting `show={false}`.
420
+
421
+ ## Props
422
+
423
+ | Prop | Type | Default | Description |
424
+ |------|------|---------|-------------|
425
+ | `show` | `boolean` | required | Visibility — controlled by the caller |
426
+ | `title` | `ReactNode` | required | Dialog title |
427
+ | `message` | `ReactNode` | required | Body message (string or JSX) |
428
+ | `onConfirm` | `() => void \| Promise<void>` | required | Fired on confirm. A returned promise drives the pending spinner; on rejection the dialog stays open and the error propagates — handle error display (toasts) inside `onConfirm` |
429
+ | `onCancel` | `() => void` | required | Fired on the Cancel button or Escape key |
430
+ | `label` | `ReactNode` | `"Confirm"` | Confirm button label |
431
+ | `variant` | `"primary" \| "danger" \| "success" \| "warning" \| "info"` | `"primary"` | Color variant for the confirm button, header dot, and message rule |
432
+ | `className` | `string` | — | Extra classes on the dialog element |
433
+
434
+ ## Behavior
435
+
436
+ - **Backdrop click does nothing** — safe for destructive confirms.
437
+ - **Escape** fires `onCancel` (ignored while a confirm is pending).
438
+ - **Async loading**: while `onConfirm`'s promise is unresolved, the confirm button shows a `spinner-border spinner-border-sm` and both buttons are disabled.
439
+ - **Accessibility**: `role="dialog"` + `aria-modal`, labelled by the title and described by the message; Tab / Shift+Tab cycle within the dialog; focus lands on the confirm button on open; body scroll is locked while shown.
440
+
441
+ ## Dark Mode
442
+
443
+ All colors use Bootstrap CSS variables — the dialog adapts automatically via `[data-bs-theme="dark"]`.
444
+ ```
445
+
446
+ - [ ] **Step 2: Add README table row**
447
+
448
+ In `README.md`, add after the `**FormRow**` row in the Components table:
449
+
450
+ ```markdown
451
+ | **ConfirmDialog** | [CONFIRMDIALOG.md](./docs/CONFIRMDIALOG.md) | Bootstrap 5 confirmation dialog with async loading state |
452
+ ```
453
+
454
+ - [ ] **Step 3: Commit**
455
+
456
+ ```bash
457
+ git add docs/CONFIRMDIALOG.md README.md
458
+ git commit -m "docs: add ConfirmDialog guide and README entry"
459
+ ```
460
+
461
+ ---
462
+
463
+ ### Task 5: Publish to npm (user action)
464
+
465
+ - [ ] **Step 1: Dry run**
466
+
467
+ Run: `cd "/media/robert/Development/Allios Apps/allios-global/react-bootstrap-plugins" && pnpm publish --dry-run`
468
+ Expected: package contents include `dist/ConfirmDialog.*`, `dist/css/plugins.css`, `dist/index.*`
469
+
470
+ - [ ] **Step 2: Publish**
471
+
472
+ Run: `pnpm publish --no-git-checks`
473
+ Expected: publishes `react-bootstrap-plugins@2.7.0` to npm
474
+
475
+ ---
476
+
477
+ ### Task 6: Portal migration (allios-swiftplus-dashboard)
478
+
479
+ **Files:**
480
+ - Modify: `/media/robert/Development/Allios Apps/allios-global/allios-swiftplus-dashboard/src/views/academics/CBCAlevelReviewResults.jsx`
481
+
482
+ - [ ] **Step 1: Remove the local ConfirmDialog component**
483
+
484
+ Locate by content (currently lines 150–166, but the file has uncommitted edits so line numbers may have shifted): delete the entire local component starting at `const ConfirmDialog = ({ show, title, message, onConfirm, onCancel, confirmLabel = 'Confirm', variant = 'primary' }) => {` through the component's closing `}` (the one after its `<Modal>...</Modal>` return, before the next top-level declaration). Do not remove anything else.
485
+
486
+ - [ ] **Step 2: Add plugin imports**
487
+
488
+ In the import block, after the existing `import AutoDisplay from 'react-bootstrap-plugins/AutoDisplay'` line, add:
489
+
490
+ ```jsx
491
+ import ConfirmDialog from 'react-bootstrap-plugins/ConfirmDialog'
492
+ import 'react-bootstrap-plugins/css/plugins.css'
493
+ ```
494
+
495
+ - [ ] **Step 3: Update the call site (around line 3664)**
496
+
497
+ Change the prop name `confirmLabel` to `label`:
498
+
499
+ ```jsx
500
+ <ConfirmDialog
501
+ show={!!deleteConfirm}
502
+ title="Delete Report Card"
503
+ message="Are you sure you want to delete this report card? This action cannot be undone."
504
+ label="Confirm Delete"
505
+ variant="danger"
506
+ onConfirm={() => handleDelete(deleteConfirm)}
507
+ onCancel={() => setDeleteConfirm(null)}
508
+ />
509
+ ```
510
+
511
+ Everything else stays — `handleDelete` is async, so the pending spinner will show while the delete API call runs.
512
+
513
+ - [ ] **Step 4: Verify no leftover references**
514
+
515
+ Run: `grep -n "confirmLabel" "/media/robert/Development/Allios Apps/allios-global/allios-swiftplus-dashboard/src/views/academics/CBCAlevelReviewResults.jsx"`
516
+ Expected: no output
517
+
518
+ - [ ] **Step 5: Commit**
519
+
520
+ ```bash
521
+ git add src/views/academics/CBCAlevelReviewResults.jsx
522
+ git commit -m "refactor: use react-bootstrap-plugins ConfirmDialog for report card deletion"
523
+ ```
524
+
525
+ ---
526
+
527
+ ### Task 7: Portal verification (user action)
528
+
529
+ - [ ] **Step 1: Bump the installed package**
530
+
531
+ Run: `cd "/media/robert/Development/Allios Apps/allios-global/allios-swiftplus-dashboard" && pnpm update react-bootstrap-plugins && grep '"react-bootstrap-plugins"' package.json`
532
+ Expected: package.json shows `^2.7.0`
533
+
534
+ - [ ] **Step 2: Smoke check in the running app**
535
+
536
+ Start the dev server (`pnpm dev` — the user runs this) and on the CBC A-Level results page open the Delete Report Card confirm dialog:
537
+
538
+ 1. Dialog appears instantly (no animation), centered, ~300px wide, 12px radius, thin border, soft shadow.
539
+ 2. Header shows a small red dot + "Delete Report Card" (semibold); body message has a 3px red left rule, muted text; footer has `Cancel` (btn-light) + `Confirm Delete` (btn-danger).
540
+ 3. Click Confirm Delete → button shows a spinner and both buttons disable while the API call runs; dialog closes on success; success toast appears.
541
+ 4. Press Escape → dialog closes (onCancel fires), no deletion.
542
+ 5. Click the dimmed backdrop → nothing happens.
543
+ 6. Tab / Shift+Tab cycle stays within the dialog.
544
+ 7. Toggle dark mode → dialog adapts (dot, rule, and buttons use theme colors).
545
+ 8. The page behind the dialog does not scroll.
546
+
547
+ - [ ] **Step 3: Done**
548
+
549
+ After the smoke check passes, the feature is complete. Tag the plugin repo: `git tag v2.7.0 && git push origin v2.7.0`.