feat(admin): notification operations — resend activation, delivery status, RBAC-scoped views
- notification-routes.js: global list (owner/admin only), per-student list (scoped via requireStudentRelationshipAccess), resend-activation (mechanically the same as regenerate — Phase 5A's token model already guarantees only the latest is ever valid, so one action covers both), a dev-only preview route (unconditionally refuses when COOKIE_SECURE=1, mirroring student-auth-routes.js's existing dev_reset_token gate). - NotificationsView.jsx: owner/admin global notification list, paginated, matching AuditView.jsx's exact convention. - StudentsView.jsx: student detail gained a Resend activation control and a Notifications history card; Credentials card now explains the automatic-email behavior. - AdminPortal.jsx: new Notifications nav entry (owner/admin only).
This commit is contained in:
@@ -10,6 +10,7 @@ import LearningView from './LearningView'
|
||||
import AssessmentsView from './AssessmentsView'
|
||||
import ClassroomView from './ClassroomView'
|
||||
import ProgramsView from './ProgramsView'
|
||||
import NotificationsView from './NotificationsView'
|
||||
import styles from './Admin.module.css'
|
||||
|
||||
// Instructor/Admin portal — clearly separate from the student experience (/admin route,
|
||||
@@ -26,6 +27,7 @@ const NAV = [
|
||||
{ key: 'classroom', label: 'Classroom' },
|
||||
{ key: 'learning', label: 'Learning' },
|
||||
{ key: 'assessments', label: 'Assessments' },
|
||||
{ key: 'notifications', label: 'Notifications', roles: ['owner', 'admin'] },
|
||||
{ key: 'instructors', label: 'Instructors', roles: ['owner'] },
|
||||
{ key: 'audit', label: 'Audit', roles: ['owner', 'admin'] },
|
||||
]
|
||||
@@ -67,6 +69,7 @@ export default function AdminPortal() {
|
||||
{view === 'classroom' && <ClassroomView />}
|
||||
{view === 'learning' && <LearningView />}
|
||||
{view === 'assessments' && <AssessmentsView role={instructor.role} />}
|
||||
{view === 'notifications' && ['owner', 'admin'].includes(instructor.role) && <NotificationsView />}
|
||||
{view === 'instructors' && instructor.role === 'owner' && <InstructorsView currentInstructorId={instructor.instructor_id} />}
|
||||
{view === 'audit' && ['owner', 'admin'].includes(instructor.role) && <AuditView />}
|
||||
{view === 'account' && <AccountView instructor={instructor} />}
|
||||
|
||||
Reference in New Issue
Block a user