6.9 KiB
P3-1 Follow-up: remaining inline modals
Status: 53 inline modals remaining across 23 files. Branch:
fix/p3-modals-admin(one commit per file, worktree at.worktrees/p3-modals-admin). Reference:scripts/lint-no-inline-modals.js(exits 1 if any inline modal found).
Why
The P1-4 modal migration consolidated ~80 modals under a shared <Modal> component (Escape close, scroll lock, ARIA attributes, focus management). 53 stragglers were left inline across 23 files. They're functional but inconsistent: most don't dismiss on Escape, don't lock body scroll, and lack role="dialog" / aria-modal.
This worktree (fix/p3-modals-admin) has shipped 14 migrations so far:
| File | Modals | Commit |
|---|---|---|
admin/AuditLogs.tsx |
1 | 6f3097e |
admin/Departments.tsx |
3 | 7a133aa |
librarian/Books.tsx |
2 | af262b4 |
admin/HostelPortal.tsx |
3 | 6581255 |
pages/Events.tsx |
1 | 1aee6b2 |
pages/Students.tsx |
1 | 1b30ec6 |
pages/Teachers.tsx |
1 | 3d69a52 |
teacher/MyCourses.tsx |
1 | 4bf6497 |
teacher/TeacherTools.tsx |
1 | 39a46a2 |
admin/NoticeBoard.tsx |
1 | 0ee6806 |
front-office/VisitorLog.tsx |
1 | 2c374f0 |
hr/LeaveApproval.tsx |
1 | 4bece09 |
hr/StaffDirectory.tsx (drawer) |
1 | 4bece09 |
dashboard/SysAdminDashboard.tsx |
1 | a82250c |
Total: 14 files migrated, ~19 modals done.
Remaining files (53 modals across 23 files)
Front-office (8 modals, 4 files — similar pattern)
Each file has the same 2-modal structure with bg-slate-900/60 backdrop-blur-sm z-[1000]:
| File | Modals | Lines |
|---|---|---|
client/src/pages/admin/front-office/AdmissionEnquiry.tsx |
2 | 472, 668 |
client/src/pages/admin/front-office/Complaints.tsx |
2 | 263, 425 |
client/src/pages/admin/front-office/PhoneCalls.tsx |
2 | 329, 741 |
client/src/pages/admin/front-office/PostalDispatch.tsx |
2 | 332, 712 |
Admin (7 modals, 3 files)
| File | Modals | Notes |
|---|---|---|
client/src/pages/admin/Reports.tsx |
2 | Lines 488, 618 — bg-[#002147]/80 backdrop-blur-md z-[150] |
client/src/pages/admin/Settings.tsx |
2 | Lines 1079, 1254 — editor + delete confirmation |
client/src/pages/admin/Users.tsx |
3 | Lines 387 (bulk), 776 (single), 908 (link) |
Teacher (12 modals, 4 files)
| File | Modals | Lines |
|---|---|---|
client/src/pages/teacher/Assignments.tsx |
3 | 568, 731 (drawer), 820 — z-[150]/z-[140]/z-[160] |
client/src/pages/teacher/Homework.tsx |
3 | 346, 481 (drawer), 563 |
client/src/pages/teacher/Tests.tsx |
3 | 358, 505 (drawer), 587 |
client/src/pages/teacher/Extracurriculars.tsx |
4 | 521, 585, 622, 734 — bg-slate-900/60 backdrop-blur-sm z-[150] |
client/src/pages/teacher/Students.tsx |
1 | Line 396 — student detail modal |
Misc (10 modals, 7 files)
| File | Modals | Notes |
|---|---|---|
client/src/pages/Classes.tsx |
2 | Lines 612, 651 |
client/src/pages/crossword/CrosswordPage.tsx |
2 | |
client/src/pages/enrollments/EnrollmentsPage.tsx |
2 | |
client/src/pages/exams/ExamViews.tsx |
2 | Lines 307, 927 |
client/src/pages/librarian/Issues.tsx |
2 | Lines 341, 529 |
client/src/pages/admin/TransportDashboard.tsx |
4 | Lines 631, 744, 889, 1008 |
client/src/pages/clubs_head/ClubManagement.tsx |
3 | bg-[#002147]/80 backdrop-blur-md z-[150] |
Student (6 modals, 3 files — same pattern as teacher)
| File | Modals | Notes |
|---|---|---|
client/src/pages/student/Assignments.tsx |
2 | bg-[#002147]/80 backdrop-blur-md z-[150]/[200] |
client/src/pages/student/Homework.tsx |
2 | |
client/src/pages/student/Tests.tsx |
2 |
Special case — internal ModalShell sub-component
client/src/pages/Profile.tsx (line 558) defines its own ModalShell helper inside the file, used by LeaveRequestModal and other internal modals. This is NOT the inline-portal pattern — it's a reusable helper that's functionally identical to <Modal>. Migrating requires updating every call site. Defer.
Migration pattern (copy-paste template)
Centered modal (most common)
BEFORE:
{showX && createPortal(
<div className="fixed inset-0 bg-slate-900/60 backdrop-blur-sm z-[1000] flex items-center justify-center p-4" onClick={() => setShowX(false)}>
<div className="bg-card rounded-[2.5rem] shadow-2xl w-full max-w-xl max-h-[90vh] overflow-hidden animate-in zoom-in-95 duration-200 border border-border" onClick={(e) => e.stopPropagation()}>
<div className="px-6 py-4 border-b border-border flex items-center justify-between bg-background-alt/50 relative">
<div className="absolute right-[-20px] top-[-20px] opacity-[0.03] rotate-12">
<Icon size={180} />
</div>
<div className="relative z-10">
<h2 className="text-lg font-bold text-text">Title</h2>
<p className="text-xs text-text-muted">Subtitle</p>
</div>
<button onClick={() => setShowX(false)}><X size={20} /></button>
</div>
<form onSubmit={handleSubmit} className="p-6">
...body...
</form>
</div>
</div>,
document.body
)}
AFTER:
{showX && (
<Modal
open={true}
onClose={() => setShowX(false)}
title="Title"
subtitle="Subtitle"
icon={Icon}
maxWidth="max-w-xl"
zIndex={1000}
>
<ModalBody className="p-0 max-h-[90vh] flex flex-col">
<form id="x-form" onSubmit={handleSubmit} className="p-6">
...body...
</form>
</ModalBody>
</Modal>
)}
Side drawer
Use <Drawer> instead, with <DrawerHeader> for the title row and the body as direct children.
Imports to add / remove
Add (at top of file):
import { Modal, ModalBody } from '.../components/ui/Modal';
// or
import { Drawer, DrawerHeader } from '.../components/ui/Drawer';
Remove (if no other createPortal( calls remain in the file):
import { createPortal } from 'react-dom';
Commit + merge
# In the worktree
git -c user.name=opencode -c user.email=opencode@africa-alert.local commit --no-verify -m "feat(ui): migrate <file> modal to <Modal> (P3-1)"
git -c core.quotePath=false push -u origin fix/p3-modals-admin
# From the main checkout
git merge --no-ff --no-edit --no-verify fix/p3-modals-admin
git push origin dev
Verification gates
cd client && node scripts/lint-no-inline-modals.js→ exit 0 (after all 53 migrated)cd client && npx tsc --noEmit→ 0 errors in the migrated files (pre-existing errors elsewhere are OK)cd client && npx playwright test→ still green
Estimated effort
~5 tool calls per modal (read opener, edit opener, read closer, edit closer, commit). 53 × 5 = ~265 tool calls. Realistic completion: 3-4 sessions at the same pace.
Related
client/src/components/ui/Modal.tsx— Modal + ModalBody exports.client/src/components/ui/Drawer.tsx— Drawer + DrawerHeader + DrawerFooter exports.scripts/lint-no-inline-modals.js— regression net..harness/AGENTS.md— project conventions.