v2 Admin Spec — Communication (New 9th Domain)
Communication
Domain 9 of the v2 navigation structure (NEW domain — IA shifts from 8+1 to 9+1 domains). Contains: Automation Emails (absorbed, enhanced), Blast Emails (NEW), Push Notifications (absorbed, enhanced), Announcement Board (NEW), Private Messages (NEW), Communication Logs (NEW). This domain is entirely new. It consolidates outbound communication previously scattered across Semester Management (Email Management) and Admin & System (Notifications), and introduces four net-new channels. Primary workflows served: WF1 (Semester Setup — welcome and onboarding emails), WF2 (Student Support — 1:1 messaging, pre-drafted TA communication), WF4 (Daily Monitoring — announcement board, urgent push notifications)
1. Domain Overview
Communication is a new top-level domain introduced in v2 (ADR-003). It unifies all outbound communication — automated emails, blast email campaigns, push notifications, announcements, and 1:1 messaging — into a single sidebar section with a consistent compose/preview/send pattern, shared recipient filtering, and a unified delivery log.
Two categories of communication live here:
- Broadcast (many recipients, one-way): Automation Emails, Blast Emails, Push Notifications, Announcement Board
- Conversational (two-way): Private Messages (bidirectional student↔teacher inbox)
The Communication Logs screen provides a unified audit trail across all six channels.
1.1 Domain Boundary: Issue Queue Stays in Admin & System
ADR-003 explicitly preserves Issue Queue (10-admin-system.md §6) in Admin & System. Issue Queue is ticketed support — students report app problems, admins triage and resolve them with a state machine (Under Review / In Progress / Resolved). It is not messaging: there is no freeform thread, no inbox paradigm, no TA routing.
Private Messages (this domain §6) is the messaging channel. If a student opens an issue ticket and then needs a conversational follow-up, the admin can initiate a Private Message from the issue detail view (Admin & System > Issue Queue > Issue Detail > "Start Message" action, deep-linking to Communication > Private Messages with prefilled recipient).
1.2 Identity Model: No Impersonation, Pre-draft Instead (ADR-004)
Admins cannot send communications "as" a TA. The original "Send as TA" proposal was rejected because it would have required dual-identity records (composed_by, sent_as), reply-routing logic, and TA-awareness notifications across every communication screen.
Instead, the domain uses a pre-draft pattern:
- Admin composes a message and selects "Draft for TA" → picks a TA from dropdown → saves draft.
- The draft is assigned to that TA's outbox.
- The TA reviews the draft in their own account (TA-facing queue is production-only; the mockup shows the admin's "Draft for TA" action but not the TA queue).
- The TA edits if desired and hits Send. The message genuinely comes from the TA: authentic sender, correct reply-to, no audit ambiguity.
Communication Logs records a single sender identity per message (the TA who actually sent it, or the admin who sent it directly). There is no composed_by / sent_as split.
Screens supporting the pre-draft pattern (per ADR-004): Private Messages, Blast Emails. Push Notifications and Announcement Board are broadcast channels that don't benefit from personal-sender framing — they always go out under the "QuranFlow" system identity or the posting admin's name.
TA permissions on Announcement Board (per STAKEHOLDER-ANSWERS 2026-04-21, default #6 CHANGED): TAs can post and manage their own announcements on the Announcement Board (edit/delete their own posts, set audience, set expiry). Moderation of other authors' posts remains admin-only. See §5 for full role visibility.
1.3 Workarounds Eliminated
| ID | Workaround | Current Tool | Replaced By |
|---|---|---|---|
| C1 | No blast email capability | External mail merge / manual per-recipient sends | Blast Emails screen (Section 3) |
| C2 | Push notifications broadcast to all users only | Settings > Notifications (no filtering) | Push Notifications with recipient filtering (Section 4) |
| C3 | No in-app announcement channel distinct from push | Ad-hoc push spam for non-urgent updates | Announcement Board (Section 5) |
| C4 | No admin↔student private messaging | WhatsApp / personal email | Private Messages (Section 6) |
| C5 | TAs needed impersonation to send "personal" reminders | Stakeholder ask during interview | Pre-draft pattern per ADR-004 (Section 6, Section 3) |
| C6 | No unified communication audit trail | Scattered logs across Stripe, app, email provider | Communication Logs (Section 7) |
| C7 | Email Management co-located with semester config (hard to find) | Semester Management > Email Management | Automation Emails in dedicated Communication domain (Section 2) |
| C8 | Sequence visibility — admin cannot see "what will this student receive next" | Manual trace through trigger conditions | Automation Emails Sequence View (Section 2.5) |
1.4 Data Architecture
Existing tables (preserved from v1):
emails: id, name, subject, body, trigger_type, trigger_config, semester_id, status, template_variables, created_at, updated_atpush_notifications: id, message, sent_by, sent_at, recipient_count (v1 table — enhanced in v2, see below)
New tables (v2 additions — to be validated during implementation):
blast_emails: id, name, subject, body, template_id, recipient_filter_json, recipient_manual_ids, scheduled_for, sent_at, sender_user_id, predraft_for_ta_id, status (Draft / Scheduled / Sent / Failed), created_by, created_atemail_templates: id, name, subject, body, template_variables, source_domain (automation / blast / unified) — unified library confirmed per STAKEHOLDER-ANSWERS (2026-04-21, OQ-C2 resolved); templates tagged bysource_domainare reusable across Automation + Blast contexts. created_by, created_at, updated_atpush_notifications(enhanced): addrecipient_filter_json,recipient_count,delivered_count,failed_count,predraft_for_ta_idannouncements: id, title, body, audience_filter_json, posted_by, posted_at, expires_at, status (Draft / Published / Expired / Deleted), read_receipt_enabledannouncement_reads: id, announcement_id, user_id, read_at (populated ifread_receipt_enabled)private_messages: id, thread_id, sender_user_id, recipient_user_id, body, attachments_json, sent_at, read_at, predraft_for_ta_idprivate_message_threads: id, subject, student_user_id, teacher_user_id, last_message_at, status (Open / Archived / Closed), created_bycommunication_logs: id, channel, source_record_id, sender_user_id, recipient_user_ids_json, subject_or_preview, status (Queued / Sent / Delivered / Failed / Read), predrafted (bool), predraft_ta_user_id (nullable), sent_at, delivered_at, failure_reason, created_at
1.5 Shared Recipient Filter (used by Sections 3, 4, 5)
Blast Emails, Push Notifications, and Announcement Board use the same recipient filter pattern. This is specified once here and referenced from each screen.
Filter dimensions (all AND-combined):
| Dimension | Source | Input |
|---|---|---|
| Audience Type | Hardcoded | Multi-select: Students, Teachers, Both |
| Gender | user_profile.gender |
Multi-select: Male / Female / Unspecified |
| Level | user_link_level_tag (current semester enrollment) |
Multi-select: 0, 1, 2, 3, 4, Year 2 |
| Mastery Course | user_profile.mastery_course or equivalent |
Multi-select dropdown of active mastery courses |
| Tags | tags via user_tags join |
Multi-select dropdown of active tags |
| Semester | semesters |
Dropdown: current semester (default) / any past semester / all |
Manual selection (alternative to filters, per stakeholder Apr 15 decision — transcript lines 274–283):
- "Select Students Manually" toggle flips the recipient selection panel into a student-search multi-select (autocomplete by name/email, add to list, remove from list). The manual list can also be pasted as comma- or newline-separated emails/names (matched to
usersrecords). - Filters and manual selection are mutually exclusive: switching modes clears the other mode's selection.
Preview bar (below filter or manual list):
- Live count: "[N] recipients selected"
- Sample names: "Sarah Ahmed, Ahmed Ali, Fatima Zahra, + [N-3] more"
- Gender/level breakdown (expandable): "Male: [X], Female: [Y] · Level 1: [A], Level 2: [B]..."
1.6 Cross-Cutting Conventions
- Status badges: all screens use the badges defined in 01-global-patterns.md §4.3, with additions: Draft (blue), Scheduled (amber), Sent (green), Delivered (green), Failed (red), Read (green), Unread (gray).
- Compose modals: follow the Modals pattern (01-global-patterns.md §4.10), max-width 800px for compose screens with preview.
- Template variables:
{{student_name}},{{semester_name}},{{level}},{{ta_name}},{{teacher_name}},{{appointment_date}}. Rendered with sample values in Preview modals. - Role visibility defaults: Admin = full; View-Only = view logs/history, no send; TA = pre-draft queue and own sent items (production-only); Support Staff = view logs, no send.
2. Screen: Automation Emails
2.1 Purpose
Configure automated email templates that fire on student-lifecycle triggers (registration, week start, semester end, promotion, failure, custom date). Absorbed from v1 Semester Management > Email Management and enhanced with a Sequence View that lets an admin search a student and see every email that student will receive in the future, in order.
The semester-scoped template model is preserved exactly — each template belongs to one semester. The semester selector (previously the page's primary layout element) becomes a filter.
Transition hint: "Formerly under Semester Management > Email Management" — shown for first 30 days.
2.2 Entry Points
- Sidebar: Communication > Automation Emails
- Semester Hub > Setup Checklist: "Configure Email Templates →" deep link (preserved from v1; now cross-domain — opens Automation Emails filtered to that semester)
- Semester Hub > Overview: "View All Emails" link in Automation section
- Dashboard: n/a (no direct tile)
2.3 Layout
Two views, toggled at top of screen: Templates (default) | Sequence View.
Templates view: Filter bar + data table (standard Data Table pattern, 01-global-patterns.md §4.1).
Sequence View: Student search box at top + vertical timeline of upcoming emails for the selected student.
2.4 Templates View
2.4.1 Filter Bar
| Filter | Type | Options |
|---|---|---|
| Search | Text input | Filters by template name or subject |
| Semester | Dropdown | All active semesters + "All semesters". Defaults to current active semester. |
| Trigger | Dropdown | All / On Registration / Week Start / Semester End / On Promotion / On Failure / Custom Date |
| Status | Dropdown | All / Active / Draft |
2.4.2 Data Displayed
| Column | Source | Sortable | Notes |
|---|---|---|---|
| Template Name | emails.name |
Yes (default: alphabetical) | Clickable → opens editor. System-seeded templates (see §2.4.5) show a lock icon next to the name. |
| Subject | emails.subject |
Yes | Truncated to 60 chars with tooltip for full text |
| Trigger | Computed from emails.trigger_type + emails.trigger_config |
Yes | "On Registration", "Week 1 start", "Semester End", "On Promotion", "On Failure", "Custom: [date]" |
| Semester | semesters.name via emails.semester_id |
No | Shown when "All semesters" is active in filter |
| Status | emails.status |
Yes | Badge: Active (green) / Draft (blue) |
| System Seeded (NEW) | emails.system_seeded (boolean) |
Yes | Lock icon if true. System-seeded templates cannot be deleted (delete action hidden/disabled with tooltip "System template — can be edited but not deleted"). |
| Last Sent (NEW) | Latest communication_logs.sent_at where source_record_id = this template |
Yes | Formatted as "Mar 8, 2026" or "Never" if unsent |
| Last Edited | emails.updated_at |
Yes | Formatted as "Mar 10, 2026" |
| Actions | — | No | Edit / Preview / Test Send / Delete / Draft for TA (icons or "..." menu). Delete is hidden/disabled on system-seeded templates. |
2.4.3 Actions
| Action | Trigger | Permission | Behavior | Confirmation |
|---|---|---|---|---|
| Create Template | "Create Template" button (top-right) | Admin | Opens email editor (modal, max-width 800px): Template Name, Subject, Trigger (dropdown with config pane per trigger type), Body (rich text with template variables {{student_name}}, {{semester_name}}, {{level}}, {{ta_name}}), Semester (dropdown), Status toggle (Active / Draft). On save: creates emails record. Toast: "Template '[name]' created." |
No |
| Edit | Edit icon or row click | Admin | Opens editor pre-filled. All fields editable. Toast on save: "Template '[name]' updated." | No |
| Preview | Preview icon (eye icon) | All | Opens preview modal (max-width 800px). Renders subject + body with sample data: {{student_name}} = "Sarah Ahmed", {{semester_name}} = current semester, {{level}} = "Level 2", {{ta_name}} = "Ustadha Amina". |
No |
| Test Send | "Test Send" button (in editor or preview) | Admin | Sends the email to the logged-in admin's email address with sample data. Toast: "Test email sent to [admin email]." | No |
| Delete | Delete icon | Admin | Destructive confirmation: "Delete template '[name]'? If this template is Active, scheduled sends using it will not be executed." On confirm: deletes emails record. Hidden/disabled on system-seeded templates (see §2.4.5) with tooltip "System template — can be edited but not deleted". |
Destructive |
| Clone from Previous | "Clone All Templates" secondary button (top-right) | Admin | Dropdown of previous semesters. Shows count ("[Previous Semester] has [N] templates."). Confirmation: "Clone all email templates from [Previous Semester]? [N] templates will be copied as Drafts." Creates independent copies with Status = Draft in current semester. Toast: "Cloned [N] templates." | Yes |
| Draft for TA (NEW) | "Draft for TA" option in "..." menu on row | Admin | Opens a one-time compose modal pre-filled with this template, plus TA selector (dropdown of all TAs). On save: creates a blast_emails record with predraft_for_ta_id set and status = Draft. Toast: "Draft saved for [TA name]." Note: this creates a one-off send, not a recurring automation — use Blast Emails for routine pre-drafted sends. |
No |
2.4.4 Templates View States
| State | Display |
|---|---|
| Has templates | Template list table |
| Empty (semester filter active) | "No email templates for [Semester Name]." + "Create Template" button + "Clone from [Previous Semester]" button |
| Empty (all semesters) | "No email templates configured." + "Create Template" button |
| Filtered — no results | "No templates match your filters." + "Clear filters" link |
| Loading | Skeleton data table |
| Error | "Unable to load email templates. Please try again." + "Retry" button |
2.4.5 System-Seeded End-of-Semester Templates (NEW)
Three system-seeded templates support End Checklist Step 3 (Send Pass/Fail Emails). These templates are system-integral (not deletable; content editable):
| Template Name | Trigger Condition | Audience | Source Domain |
|---|---|---|---|
| End of Semester — Pass (L1–L4) | End Checklist Step 3 fires for a student with final_status = 'pass' AND level IN (1,2,3,4) |
Passing L1–L4 students | automation (system-seeded) |
| End of Semester — Fail (L1–L4, Opt-in Repeat) | End Checklist Step 3 fires for a student with final_status = 'fail' AND level IN (1,2,3,4) |
Failing L1–L4 students. Contains opt-in-to-repeat link. Students are NOT automatically re-enrolled — they must follow the link. Opt-in is fully self-service; no admin approval is required (STAKEHOLDER-ANSWERS-2026-04-22 §C Q2). The link directs the student to the repeat-discount checkout (08-billing-payments.md §2.10) which cross-references the per-semester repeat-eligible list. |
automation (system-seeded) |
| End of Semester — Mastery Elective Prompt | End Checklist Step 3 fires for a Mastery student (level_tag.is_mastery = 1) |
Mastery students. Prompts signup for same or different elective next semester. | automation (system-seeded) |
Note: These three templates are system-seeded and cannot be deleted, but their subject lines, bodies, and template variables are admin-editable. Trigger conditions are fixed (driven by End Checklist Step 3). System-seeded templates show a lock icon next to the name in the Templates view and their Delete action is hidden/disabled with tooltip "System template — can be edited but not deleted".
Mockup scope: static mock templates with placeholder bodies; triggering is mock-only.
2.5 Sequence View (NEW)
2.5.1 Purpose
Answer the question: "What emails will this student receive in the future, and when?" Stakeholder ask: "Search a student, see every email they will receive with exact delivery dates."
2.5.2 Layout
Student search at top (autocomplete, as per Global Search §2.2, scoped to students only) → on selection, renders a vertical timeline below.
2.5.3 Timeline Content
Each upcoming email the selected student will receive, based on:
- Their current enrollment (
user_link_level_tag) - The current semester's Active templates
- Evaluated trigger conditions (e.g., "Week 3 start" → if today is Mar 1 and Week 3 starts Mar 15, this email is 14 days out)
| Timeline Item Field | Source | Notes |
|---|---|---|
| Scheduled Date | Computed from trigger + semester calendar | "Mar 15, 2026 at 9:00 AM EST" or relative "in 14 days" |
| Template Name | emails.name |
Clickable → opens Preview modal |
| Trigger | Computed | "Week 3 start" / "On promotion" / "Semester end" etc. |
| Subject | emails.subject |
Truncated |
| Preview | Rendered subject + first 200 chars of body with student's values substituted | Expandable inline |
Open question (OQ-C1): whether Sequence View shows only the selected student's upcoming emails (current assumption) or a sample student as a preview when no student is searched. Per ADR-003, mockup defaults to searched student.
2.5.4 Sequence View Actions
| Action | Trigger | Permission | Behavior | Confirmation |
|---|---|---|---|---|
| Search Student | Type in search box | Admin, View-Only | Autocomplete suggestions. On select: loads timeline. | No |
| Preview Email | Click timeline item | All | Opens preview modal with this student's values substituted (real {{student_name}}, actual {{ta_name}}, etc.) |
No |
| Send Now | "Send Now" button on timeline item | Admin | Confirmation: "Send '[template name]' to [student name] now? This is outside the normal trigger schedule." On confirm: sends immediately, logs to Communication Logs. Does not remove the scheduled send. | Yes |
| Skip This Email | "Skip" button on timeline item | Admin | Confirmation: "Skip sending '[template name]' to [student name]? This will not affect other students." On confirm: creates a skip record (production: email_skips table) so this specific template will not fire for this student. |
Yes |
2.5.5 Sequence View States
| State | Display |
|---|---|
| No student selected | "Search for a student above to see their email sequence." + empty-state illustration |
| Student selected, has upcoming emails | Timeline with 1–N items |
| Student selected, no upcoming emails | "[Student name] has no upcoming automated emails." + hint: "This may mean all templates are Drafts or the student is not enrolled." |
| Student selected, not enrolled | "[Student name] is not enrolled in the current semester. Switch semester filter to see past sequences." |
| Loading | Skeleton timeline |
| Error | "Unable to compute sequence. Please try again." + "Retry" |
2.6 End Checklist Email Queue (NEW)
2.6.1 Purpose
Per-student queue of End Checklist Step 3 emails that have not yet been sent (or that failed/bounced). Shows each enrolled student's current final_status, the email template that applies to their track (pass / fail-opt-in / mastery-elective), the send status (Unsent / Queued / Sent / Failed / Bounced), and a per-student "Send Now" button to manually trigger the correct template. Provides admin a clear view of who is and isn't emailed during semester close, and a manual fallback for any student the automatic trigger missed.
2.6.2 Scope
All enrolled students in the closing semester. Branched by track:
- L1–L4 pass (
final_status = 'pass' AND level IN (1,2,3,4)) → uses "End of Semester — Pass (L1–L4)" template. - L1–L4 fail (
final_status = 'fail' AND level IN (1,2,3,4)) → uses "End of Semester — Fail (L1–L4, Opt-in Repeat)" template. - Mastery (
level_tag.is_mastery = 1) → uses "End of Semester — Mastery Elective Prompt" template. - Students in L1–L4 with
final_status IS NULLappear in the queue with status "Awaiting Pass/Fail decision" (Set Pass/Fail deep-link → 03-student §2); they cannot be emailed until Step 2 sets their status.
Leavers are excluded (per STAKEHOLDER-ANSWERS-2026-04-21: "they handle their own enrollment and payment").
2.6.3 Entry Points
- Semester Hub > End Checklist > Step 3 ("Send Pass/Fail Emails"): deep link with URL
?from=end-checklist&semester=[id]&filter=unsent. When?from=end-checklistis present, the screen shows a persistent "← Return to End Checklist for [Semester Name]" banner; clicking returns to the End Checklist tab with scroll position preserved. - Sidebar: Communication > Automation Emails > "End Checklist Queue" tab (visible only when the semester filter is on a semester in Close phase).
2.6.4 Data Displayed
| Column | Source | Sortable | Filterable | Format |
|---|---|---|---|---|
| Student Name | users.name via enrollment |
Yes (default: alphabetical) | Yes (search) | Clickable → Student Detail |
| Level | level_tag.name via user_link_level_tag |
Yes | Yes (dropdown: Level 1 / Level 2 / Level 3 / Level 4 / Mastery) | Level badge |
| Final Status | user_link_level_tag.final_status for closing semester |
Yes | Yes (dropdown: Pass / Fail / Mastery Passed / Not Set) | Badge: Pass (green) / Fail (red) / Mastery Passed (blue) / Not Set (gray) |
| Template | Derived from track (see §2.6.2) | No | Yes (dropdown: Pass / Fail / Mastery Elective) | Template name (clickable → opens the template in §2.4 for reference) |
| Send Status | communication_logs.status for this student's End Checklist Step 3 email, or "Unsent" if no record exists |
Yes | Yes (dropdown: Unsent / Queued / Sent / Failed / Bounced — default: Unsent + Failed + Bounced) | Badge: Unsent (gray) / Queued (amber) / Sent (green) / Failed (red) / Bounced (red) |
| Last Attempt | Timestamp of most recent send attempt (or delivery) | Yes | No | "Mar 15, 2026 at 11:02 AM" or "—" if never attempted |
| Action | — | No | No | "Send Now" button (Unsent/Failed/Bounced) / "View Log" link (Sent) / "Awaiting Pass/Fail" pill + Set-Pass/Fail deep-link (Not Set) |
2.6.5 Filter Bar
| Filter | Type | Options |
|---|---|---|
| Search | Text input | Student name or email |
| Level | Dropdown | All / Level 1 / Level 2 / Level 3 / Level 4 / Mastery |
| Template | Dropdown | All / Pass / Fail / Mastery Elective |
| Send Status | Dropdown | All / Unsent / Queued / Sent / Failed / Bounced (default: Unsent + Failed + Bounced — the "work to do" subset) |
2.6.6 Actions
| Action | Trigger | Permission | Behavior |
|---|---|---|---|
| Send Now (per-student) | "Send Now" button on an Unsent/Failed/Bounced row | Admin | Queues the student's correct End Checklist template for send (routed by their track per §2.6.2). On success: updates communication_logs with Queued → Sent; updates row. Toast: "[Template name] queued for [student name]." On failure (provider error): status → Failed with a failure reason shown on hover. |
| Send Pending Emails (bulk) | Checkbox selection + "Send Pending Emails" button in the bulk action bar | Admin | Queues all selected Unsent/Failed/Bounced students (each routed to their correct template). Confirmation: "Send pending End Checklist emails to [N] students? ([X] pass, [Y] fail, [Z] mastery)." Progress indicator during batch. On completion shows summary. |
| Retry Failed (filter shortcut) | "Retry all Failed" button (top-right, visible when any Failed rows present) | Admin | Shortcut for selecting all Failed and running Send Pending Emails. |
| View Log (per-student) | "View Log" link on a Sent row | Admin, View-Only | Opens Communication Logs (§7) filtered to this student + End Checklist email entry. |
| Set Pass/Fail deep-link (per-student) | "Awaiting Pass/Fail" pill on a Not Set row | Admin; TA (own students) | Navigates to 03-student §2 Students list filtered to this student with Set Pass/Fail ready. Returns here after status is set. |
| Return to End Checklist | Persistent banner at top when ?from=end-checklist is set |
All roles with queue access | "← Return to End Checklist for [Semester Name]." Clicking returns to 04-semester-management.md §3.8 with scroll position preserved. |
2.6.7 States
| State | Display |
|---|---|
| All sent | Green status bar: "All [N] enrolled students have been emailed for [Semester Name]." Step 3 in End Checklist auto-completes. |
| In progress | Mix of Unsent / Queued / Sent / Failed rows. Counts summarized above the table: "[X] Unsent · [Y] Queued · [Z] Sent · [W] Failed · [Q] Bounced". |
| Awaiting Pass/Fail | Students in L1–L4 with final_status IS NULL appear with an "Awaiting Pass/Fail" pill; they are blocked from send until Step 2 sets status. |
| Not yet applicable | If semester phase is Setup or early Active: informational message "End Checklist Step 3 fires during semester close. No sends expected yet." Queue is still viewable. |
| Loading | Skeleton queue rows. |
| Error | "Unable to load Email Queue. Please try again." + "Retry" button. |
2.6.8 Role Visibility
- Admin: Full queue + per-student Send Now + bulk send + View Log + Set Pass/Fail deep-link.
- View-Only: Queue view + View Log + Last Attempt timestamps. No send actions.
- TA: Hidden from sidebar tab; accessible only via the Set Pass/Fail deep-link return flow when completing own students' status. TA cannot trigger end-of-semester email sends — the automated system sends; admin does manual fallback.
- Support Staff: Queue view + View Log. No send actions.
2.7 Role Visibility
- Admin: Full CRUD on templates + Sequence View + all actions (Create, Edit, Delete, Preview, Test Send, Clone, Draft for TA, Send Now, Skip)
- View-Only: View templates list + Preview + Sequence View (read-only). No Create, Edit, Delete, Test Send, Clone, Draft for TA, Send Now, Skip.
- TA: Hidden (sidebar item not visible). TA sees pre-drafted items in their own outbox (production-only).
- Support Staff: View templates list + Preview + Sequence View (useful for answering "why did this student get this email?"). No send or edit actions.
3. Screen: Blast Emails (NEW)
3.1 Purpose
Compose and send one-off emails to a filtered or manually selected list of students/teachers. Unlike Automation Emails (event-triggered, per-student), Blast Emails are admin-initiated, multi-recipient, one-time sends. Supports pre-drafting for TAs (ADR-004).
3.2 Entry Points
- Sidebar: Communication > Blast Emails
- Dashboard: "Send Blast Email" quick action (Admin only)
- Student Management > Students list: "Email Selected" bulk action (with recipients pre-populated from selection)
3.3 Layout
Two views: Campaigns (list of past/scheduled/draft blasts, default) | Compose (full-screen compose).
3.4 Campaigns View
3.4.1 Filter Bar
| Filter | Type | Options |
|---|---|---|
| Search | Text input | Filters by campaign name or subject |
| Status | Dropdown | All / Draft / Scheduled / Sent / Failed |
| Sender | Dropdown | All / Me / [list of admins] |
| Date Range | Date range picker | Sent date range |
Note on "Scheduled" records: Scheduling is production-only per STAKEHOLDER-ANSWERS-2026-04-21 (default #5). The Schedule button is removed from the Compose view (§3.5.3), so the mockup cannot create Scheduled campaigns. The Status filter,
Scheduled Forcolumn, and Cancel Scheduled action are retained for forward-compatibility with production (where Scheduled records will exist) but mockup fixtures will contain no Scheduled rows.
3.4.2 Data Displayed
| Column | Source | Sortable | Notes |
|---|---|---|---|
| Campaign Name | blast_emails.name |
Yes | Clickable → opens detail/preview |
| Subject | blast_emails.subject |
Yes | Truncated to 60 chars |
| Recipients | Count from blast_emails.recipient_filter_json evaluation or recipient_manual_ids.length |
Yes | "[N] recipients" |
| Status | blast_emails.status |
Yes | Badge: Draft (blue) / Scheduled (amber) / Sent (green) / Failed (red) |
| Sender | users.name via blast_emails.sender_user_id, or "[TA Name] (pre-drafted by [admin])" if predraft_for_ta_id set |
Yes | Shows true sender identity per ADR-004 |
| Scheduled For | blast_emails.scheduled_for |
Yes | "Mar 15, 2026 9:00 AM" or "—" if sent-immediately |
| Sent At | blast_emails.sent_at |
Yes (default: descending) | Formatted date/time or "—" if unsent |
| Actions | — | No | View / Duplicate / Delete / Cancel Scheduled (icons or "..." menu) |
3.4.3 Campaign Actions
| Action | Trigger | Permission | Behavior | Confirmation |
|---|---|---|---|---|
| Compose Blast | "Compose Blast" button (top-right) | Admin | Switches to Compose view with empty form | No |
| View | Row click or View icon | All | Opens read-only detail: campaign metadata + rendered email + recipient list + delivery status breakdown | No |
| Duplicate | Duplicate icon | Admin | Opens Compose view pre-filled with this campaign's subject/body/recipient filter. Campaign Name gets " (copy)" suffix. | No |
| Delete (Draft only) | Delete icon | Admin | Destructive confirmation: "Delete draft '[name]'? This cannot be undone." | Destructive |
| Cancel Scheduled | "Cancel Schedule" icon (Scheduled status only) | Admin | Confirmation: "Cancel scheduled send of '[name]' ([N] recipients)? The email will not be sent." On confirm: sets status = Draft, clears scheduled_for. |
Yes |
3.4.4 Campaigns View States
| State | Display |
|---|---|
| Has campaigns | Data table with campaigns |
| Empty | "No blast email campaigns yet." + "Compose Blast" button |
| Filtered — no results | "No campaigns match your filters." + "Clear filters" link |
| Loading | Skeleton data table |
| Error | "Unable to load campaigns." + "Retry" button |
3.5 Compose View
Three-section layout: Recipients (top) → Message (middle) → Review & Send (bottom, sticky).
3.5.1 Recipients Section
Uses the Shared Recipient Filter defined in Section 1.5. Mode toggle: "Filter by Criteria" (default) | "Select Manually".
Live preview bar at bottom of section: "[N] recipients selected. Sarah Ahmed, Ahmed Ali, +12 more."
Save as Audience (optional): "Save this selection as a named audience" — persists the filter JSON for reuse across blasts. Named audiences appear in a dropdown above the filter: "Use Saved Audience →". Open question (OQ-C4): are saved audiences shared across admins or private per admin.
3.5.2 Message Section
| Field | Type | Notes |
|---|---|---|
| Campaign Name | Text input | Internal label; not shown to recipients. Required. |
| Template | Dropdown | "Start from scratch" (default) / named templates from email_templates filtered by source_domain in ('blast', 'unified', 'automation') — unified library confirmed per STAKEHOLDER-ANSWERS 2026-04-21. Templates tagged by source_domain are reusable across Automation + Blast contexts. |
| Subject | Text input | Supports template variables. Required. |
| Body | Rich text editor | Template variables, links, images (basic formatting). Required. |
| From | Display only | "QuranFlow noreply@quranflow.com" — not editable (no identity switching per ADR-004). Shows "Pre-drafted for [TA name]" tag if admin is using Draft for TA action. |
| Reply-to | Display only | Admin's email, or TA's email if pre-drafted for TA |
| Save as Template | Checkbox | If checked, on send: also creates an email_templates record with the composed subject/body. Requires template name input. |
3.5.3 Review & Send Section (sticky footer)
| Control | Behavior |
|---|---|
| Preview | Opens preview modal showing the rendered email with the first recipient's substituted values + pagination to preview with other recipients' values |
| Test Send | Sends a test to the admin's own email using the first recipient's values |
| Save Draft | Saves the current state as blast_emails with status = Draft. Toast: "Draft saved." |
| Send Now | Destructive confirmation: "Send '[campaign name]' to [N] recipients now? This cannot be undone." On confirm: immediately queues for send. Logs to Communication Logs (Section 7). |
| Draft for TA | Opens TA selector dropdown. On confirm: saves as blast_emails with predraft_for_ta_id set, status = Draft. Toast: "Draft saved for [TA name]. They will see it in their outbox." (TA-side queue is production-only per ADR-004.) |
Scheduled sends deferred to production per STAKEHOLDER-ANSWERS 2026-04-21 (default #5). v2 mockup supports Send Now and Save Draft only; scheduled publishing will be added in production. The
blast_emails.scheduled_forcolumn is preserved in the data model for production use; only the UI Schedule button is removed from the mockup.
3.5.4 Compose View Actions (full table)
| Action | Trigger | Permission | Behavior | Confirmation |
|---|---|---|---|---|
| Preview | "Preview" button in sticky footer | Admin | Modal with rendered email, recipient pagination, and "Send" shortcut | No |
| Test Send | "Test Send" button | Admin | Sends to admin's own email. Toast: "Test email sent." | No |
| Save Draft | "Save Draft" button | Admin | Creates/updates blast_emails with status = Draft. Toast. |
No |
| Send Now | "Send Now" button (primary) | Admin | Destructive confirmation with recipient count summary. Logs to Communication Logs. | Destructive |
| Draft for TA | "Draft for TA" secondary button | Admin | TA dropdown → save as pre-draft. TA takes over final send. | Yes |
| Cancel | "Cancel" link (top of form) | Admin | If dirty: "Discard changes? Your draft will not be saved." Otherwise returns to Campaigns view. | If dirty |
3.6 Role Visibility
- Admin: Full compose, send, pre-draft, and campaign management. Scheduling is production-only (see §3.4.1 note).
- View-Only: View Campaigns list + View individual campaigns (read-only). Cannot Compose, Send, Duplicate, Delete, or Draft for TA.
- TA: Hidden (sidebar item not visible). TA sees their pre-drafted blasts in a production-only outbox.
- Support Staff: View Campaigns list + View individual campaigns. No send actions.
4. Screen: Push Notifications
4.1 Purpose
Send push notifications to the mobile app. Absorbed from v1 Admin & System > Notifications and enhanced with the shared recipient filter (Section 1.5), delivery tracking (sent/delivered/failed counts), and a history view. Push notifications are reserved for urgent/time-sensitive communications (urgent session changes, emergency program updates).
Transition hint: "Formerly under Admin & System > Notifications" — shown for first 30 days.
4.2 Entry Points
- Sidebar: Communication > Push Notifications
- Dashboard: "Send Notification" quick action (retargeted from v1 Admin & System > Notifications per ADR-003)
4.3 Layout
Two-pane: Compose (top, sticky form) | History (below, data table of past notifications).
4.4 Compose Pane
4.4.1 Fields
| Field | Type | Notes |
|---|---|---|
| Message | Text area | Plain text only (no rich formatting — push notifications are short). Character counter 0/200. Required. |
| Recipients | Shared Recipient Filter (§1.5) | Filter-by-criteria or manual selection. Live preview. Required. |
| Title (optional) | Text input | Max 50 chars. If omitted: defaults to "QuranFlow" in the notification payload. |
| Deep Link (optional) | Dropdown + dynamic field | "None" / "Open Student Detail" / "Open Lesson" / "Open Live Session" / "Custom URL". Determines what the app opens when the user taps the notification. |
4.4.2 Actions
| Action | Trigger | Permission | Behavior | Confirmation |
|---|---|---|---|---|
| Preview | "Preview" button | Admin | Shows a mobile-notification-card preview with the composed title + message rendered | No |
| Send Now | "Send Now" button (primary) | Admin | Destructive confirmation: "Send push notification to [N] recipients now? This cannot be undone." On confirm: queues via push provider (APNs/FCM), creates push_notifications record, logs to Communication Logs. |
Destructive |
| Clear Form | "Clear" link | Admin | Confirms if dirty | If dirty |
Scheduled sends deferred to production per STAKEHOLDER-ANSWERS 2026-04-21 (default #5). v2 mockup supports Send Now only; scheduled publishing will be added in production. The
push_notifications.scheduled_forcolumn is preserved in the data model for production use; only the UI Schedule button is removed from the mockup.
4.5 History Pane
4.5.1 Filter Bar
| Filter | Type | Options |
|---|---|---|
| Search | Text input | Filters by message content |
| Status | Dropdown | All / Scheduled / Sent / Failed |
| Sender | Dropdown | All / Me / [list of admins] |
| Date Range | Date range picker | Sent date range |
Note on "Scheduled" records: Scheduling is production-only per STAKEHOLDER-ANSWERS-2026-04-21 (default #5). The Schedule button is removed from the Compose view (§4.4.2), so the mockup cannot create Scheduled push notifications. The Status filter and Cancel Scheduled action are retained for forward-compatibility with production.
4.5.2 Data Displayed
| Column | Source | Sortable | Notes |
|---|---|---|---|
| Sent At | push_notifications.sent_at |
Yes (default: descending) | "Mar 8, 2026 2:14 PM" |
| Message | push_notifications.message |
No | Truncated to 80 chars, tooltip for full |
| Sender | users.name via push_notifications.sent_by |
Yes | Admin name |
| Recipients | push_notifications.recipient_count |
Yes | "247 recipients" |
| Delivered | push_notifications.delivered_count / recipient_count |
Yes | "245/247 (99%)" |
| Failed | push_notifications.failed_count |
Yes | Red text if >0 |
| Status | Derived from delivered/failed counts | Yes | Badge: Sent (green) / Partial (amber, if failed > 0) / Failed (red, if delivered = 0) |
| Actions | — | No | View / Duplicate (icons) |
4.5.3 History Actions
| Action | Trigger | Permission | Behavior | Confirmation |
|---|---|---|---|---|
| View | Row click or View icon | All | Opens detail modal: full message, recipient breakdown, delivery status per recipient segment (Admin only sees failures with reasons) | No |
| Duplicate | Duplicate icon | Admin | Pre-fills compose pane with this notification's message + recipient filter | No |
| Cancel Scheduled | "Cancel" icon (Scheduled only) | Admin | Confirmation: "Cancel scheduled notification to [N] recipients?" On confirm: sets status = Cancelled. | Yes |
4.6 States
| State | Display |
|---|---|
| Compose — filter producing 0 recipients | Warning below recipient filter: "No students match these filters. Adjust filters or switch to manual selection." Send Now disabled. |
| Compose — ready | Live recipient count + sample names above Send button |
| History — has notifications | Data table |
| History — empty | "No push notifications sent yet." |
| Loading | Skeleton form (compose) + skeleton table (history) |
| Error — provider failure | Compose retains input; banner: "Push provider unreachable. Your notification was not sent. [Retry]" |
4.7 Role Visibility
- Admin: Full compose + send + history view. Scheduling is production-only (see §4.5.1 note).
- View-Only: View History only. No Compose pane shown (or shown disabled).
- TA: Hidden (sidebar item not visible)
- Support Staff: View History only. No Compose pane.
5. Screen: Announcement Board (NEW)
5.1 Purpose
Post in-app announcements to students and/or teachers. Replaces the community board concept with a one-way, authored channel. Used for non-urgent program updates, surveys, fix notes, policy changes — things that don't warrant a push notification but should be visible to users when they open the app.
Admins and TAs can post to the Announcement Board (per STAKEHOLDER-ANSWERS 2026-04-21, default #6 CHANGED). TAs can manage their own posts (edit/delete/set audience/set expiry). Moderation of others' posts is admin-only.
Students see the announcement in a dedicated in-app announcement area. Teachers (TAs) see posts targeted to them similarly.
5.2 Entry Points
- Sidebar: Communication > Announcement Board
- Dashboard: "Post Announcement" quick action
5.3 Layout
Two-pane: Active Announcements (top, card list of currently visible posts) | All Posts (below, full history table with filter bar).
5.4 Active Announcements Pane
A card list of announcements where status = Published AND expires_at is in the future (or null).
Each card shows:
- Title (bold)
- Body preview (first 200 chars)
- Audience summary (e.g., "All students" or "Level 2 + Level 3 students")
- Posted date + author
- Expiry countdown ("Expires in 5 days" / "No expiry")
- Actions: Edit / Delete / Expire Now / View Read Receipts (if enabled)
5.5 All Posts Pane
5.5.1 Filter Bar
| Filter | Type | Options |
|---|---|---|
| Search | Text input | Filters by title or body |
| Status | Dropdown | All / Draft / Published / Expired / Deleted |
| Audience | Dropdown | All / Students / Teachers / Both |
| Posted By | Dropdown | All / Me / [list of admins] |
| Date Range | Date range picker | Posted date range |
5.5.2 Data Displayed
| Column | Source | Sortable | Notes |
|---|---|---|---|
| Title | announcements.title |
Yes | Clickable → opens edit/view modal |
| Audience | announcements.audience_filter_json summary |
No | "All students", "Level 1 + 2 students", "All teachers", etc. |
| Posted By | users.name via announcements.posted_by |
Yes | Author name — author_user_id/posted_by can reference an admin or a TA per STAKEHOLDER-ANSWERS 2026-04-21 (no schema change). |
| Posted At | announcements.posted_at |
Yes (default: descending) | "Mar 8, 2026" |
| Expires At | announcements.expires_at |
Yes | "Mar 15, 2026" or "No expiry" |
| Read Receipts | Count from announcement_reads / recipient count if read_receipt_enabled |
No | "134/247 (54%)" or "—" if disabled |
| Status | announcements.status |
Yes | Badge: Draft (blue) / Published (green) / Expired (gray) / Deleted (gray) |
| Actions | — | No | Edit / Delete / Expire Now / View Read Receipts |
5.6 Post Announcement Compose Modal
| Field | Type | Notes |
|---|---|---|
| Title | Text input | Max 80 chars. Required. |
| Body | Rich text editor | Links + basic formatting. Required. Template variables NOT supported (announcements are broadcast, not per-student). |
| Audience | Shared Recipient Filter (§1.5) | Filter-by-criteria only (no manual selection for announcements — announcements are broadcast by definition). |
| Expires At | Date/time picker | Optional. If null: announcement remains visible until manually expired/deleted. |
| Read Receipts | Toggle | Default: off. When on, tracks per-user read via announcement_reads table. Open question (OQ-C5). |
| Status | Toggle | Draft / Published (default on Publish button press) |
5.7 Actions
| Action | Trigger | Permission | Behavior | Confirmation |
|---|---|---|---|---|
| Post Announcement | "Post Announcement" button (top-right) | Admin or TA | Opens compose modal. On Publish: creates announcements record with status = Published, posted_at = now, posted_by = current user (admin or TA). Logs to Communication Logs. Toast: "Announcement posted to [N] users." |
No (but summary shown in modal footer) |
| Save Draft | "Save Draft" button in compose modal | Admin or TA | Status = Draft. Not visible to users. Toast: "Draft saved." | No |
| Edit | Edit icon | Admin (any post); TA (own posts only) | Opens compose modal pre-filled. Warning if Published: "Editing a published announcement will update it in real time for all [N] users currently viewing." | Yes if Published |
| Delete | Delete icon | Admin (any post); TA (own posts only) | Destructive confirmation: "Delete '[title]'? Users will no longer see this announcement. Read receipts will be preserved." On confirm: sets status = Deleted. | Destructive |
| Expire Now | "Expire Now" action | Admin (any post); TA (own posts only) | Confirmation: "Expire '[title]' now? It will no longer be shown to users, but remains in history." On confirm: sets expires_at = now. |
Yes |
| View Read Receipts | "View Read Receipts" action (if enabled) | Admin | Opens modal with recipient list, read/unread status, read timestamp | No |
| Post to Both Audiences | Audience selector in compose | Admin | If audience = "Both", creates a single announcement record with broad audience filter; no duplicate post. | No |
5.8 States
| State | Display |
|---|---|
| Has active announcements | Active pane shows cards; All Posts shows table |
| No active announcements | Active pane: "No active announcements." + "Post Announcement" button. All Posts table may still have history. |
| Empty (no posts ever) | "No announcements posted yet." + "Post Announcement" button |
| Loading | Skeleton cards (active) + skeleton table (history) |
| Error | "Unable to load announcements." + "Retry" |
| Read receipts disabled for a post | Read Receipts column shows "—" |
5.9 Role Visibility
- Admin: Full posting, full moderation. CRUD on any post (Post, Edit, Delete, Expire, View Read Receipts). Sidebar: visible.
- View-Only: View Active Announcements + All Posts table (read-only). No Post, Edit, Delete, Expire actions. View Read Receipts accessible.
- TA: Can post and manage their own posts (edit/delete/expire/set audience/set expiry their own). Cannot moderate or edit others' posts. Cannot view draft posts from other authors. Can read all published posts. Sidebar: visible. (Per STAKEHOLDER-ANSWERS 2026-04-21, default #6 CHANGED.)
- Support Staff: View Active + All Posts (read-only). Useful context for student inquiries ("what was just announced?").
6. Screen: Private Messages (NEW)
6.1 Purpose
Bidirectional conversational messaging between admins, teachers (TAs), and students. Replaces ad-hoc WhatsApp/email for student support and teacher-student check-ins. This is the core screen for the pre-draft pattern (ADR-004): an admin can compose a message, assign it as a draft to a TA, and the TA sends it from their own account.
Key design points (per ADR-003, ADR-004):
- Bidirectional — students can initiate, teachers can initiate, admins can initiate.
- No impersonation — every message has a single sender identity.
- Admins can view all conversations (moderation/support) but cannot impersonate participants.
- Admin pre-draft flow: admin composes a message targeted to a specific student, selects a TA, saves as draft. The TA's account picks it up and they send.
6.2 Entry Points
- Sidebar: Communication > Private Messages
- Student Detail > Profile tab: "Message" action (initiates a new thread or opens existing)
- Teacher Detail > Profile tab: "Pre-draft for [TA]" action (opens compose with TA locked as sender)
- Admin & System > Issue Queue > Issue Detail: "Start Message" action (initiates thread with issue reporter)
6.3 Layout
Three-pane inbox layout:
- Left: Conversation list (threads)
- Middle: Selected thread — message list (scrollable, newest at bottom) + reply composer at bottom
- Right (contextual, admin only): Participant context panel (student/TA profile summary, recent submissions, recent payments — helpful for support conversations)
6.4 Conversation List (left pane)
6.4.1 Filter Bar
| Filter | Type | Options |
|---|---|---|
| Search | Text input | Filters by participant name or message content |
| View | Dropdown | All conversations / My conversations / Pre-drafts I created / Unread |
| Student | Dropdown (autocomplete) | Filters to threads involving a specific student |
| Teacher | Dropdown | Filters to threads involving a specific TA |
| Status | Dropdown | All / Open / Archived / Closed |
6.4.2 Thread Card Fields
| Field | Source | Notes |
|---|---|---|
| Participants | users.name for student + teacher/admin |
"Sarah Ahmed ↔ Ustadha Amina" |
| Subject | private_message_threads.subject |
Bold if unread |
| Last Message Preview | First 60 chars of most recent message | Truncated |
| Last Message At | private_message_threads.last_message_at |
"2h ago" or "Mar 8" |
| Unread Badge | Count of messages where read_at IS NULL for viewer |
Blue dot or count badge |
| Pre-draft Indicator | If any message in thread has predraft_for_ta_id |
Small "Pre-drafted" tag |
| Status | private_message_threads.status |
Badge if Archived/Closed |
6.5 Thread View (middle pane)
6.5.1 Header
| Element | Content |
|---|---|
| Thread subject | Editable by admin (inline) |
| Participants | "Sarah Ahmed (Student) · Ustadha Amina (TA)" |
| Actions | Archive / Close / Reopen / Delete (admin only) / Add Participant (admin only, OQ-C7) |
6.5.2 Message List
Each message rendered as a bubble aligned by sender (viewer's messages right, others left).
| Field | Source | Notes |
|---|---|---|
| Sender Name | users.name via private_messages.sender_user_id |
Shown above bubble |
| Sender Role | Derived from user_type |
"(TA)", "(Admin)", "(Student)" suffix |
| Body | private_messages.body |
Rich text, links, line breaks |
| Attachments | private_messages.attachments_json |
Inline thumbnails for images; download links for other files. Open question (OQ-C8). |
| Sent At | private_messages.sent_at |
"2:14 PM" or "Mar 8, 2:14 PM" |
| Read Status | private_messages.read_at on recipient side |
"Read 2:16 PM" / "Delivered" / "Sent" |
| Pre-draft Tag | If predraft_for_ta_id was populated before the TA sent |
Small tag: "Pre-drafted by [admin name]" visible to admin only |
6.5.3 Reply Composer (sticky bottom)
| Field | Type | Notes |
|---|---|---|
| Message body | Rich text area | Basic formatting, links, attachments |
| Attach | Button | Open question (OQ-C8): what file types, what max size. Mockup scope: UI affordance only. |
| Send | Primary button | Sends as the viewing admin/TA. No identity switching. |
| Draft for TA | Secondary button (admin only) | Opens TA selector. Saves as draft targeted to selected TA. Toast: "Draft saved for [TA]. They'll see it in their outbox." (TA outbox production-only.) |
6.6 Admin Context Panel (right pane, admin view only)
When an admin is viewing a thread, a right-side panel shows context about the student participant:
| Section | Content |
|---|---|
| Student Summary | Name, level, semester, TA assignment, status badge |
| Recent Submissions | Last 3 submissions with status (links to Student Detail > Submissions) |
| Recent Payments | Last 3 payment events with status (links to Student Detail > Payments) |
| Related Issues | Open issues from Issue Queue involving this student (link) |
This panel is view-only and does not appear for the TA view (TAs already have student context in their own UI).
6.7 Compose New Thread
Triggered from "New Message" button in conversation list, or from cross-domain entry points (Student Detail, Teacher Detail).
| Field | Type | Notes |
|---|---|---|
| Recipient (Student) | Autocomplete | Required. One student per thread. |
| Recipient (Teacher/Admin) | Autocomplete | Required if admin is creating on behalf (pre-draft flow). Prefilled if launched from Teacher Detail. |
| Subject | Text input | Required. |
| Body | Rich text | Required. |
| Send As | Display only | The logged-in admin's identity, or "Draft for [TA]" if pre-drafting. No impersonation. |
Actions: Send (immediate, creates thread + first message) / Save Draft (draft thread, not visible to recipient) / Draft for TA (saves as pre-draft; TA sends first message from their own account).
6.8 Actions (thread-level)
| Action | Trigger | Permission | Behavior | Confirmation |
|---|---|---|---|---|
| New Message | "New Message" button (conversation list header) | Admin | Opens compose new thread form | No |
| Send Reply | Send button in composer | Thread participant or admin | Appends message, updates last_message_at, marks unread for other participants, logs to Communication Logs |
No |
| Draft for TA | "Draft for TA" button in composer | Admin | TA selector → saves as pre-draft. Thread not yet visible to student until TA sends. Toast: "Draft saved for [TA]." | Yes |
| Archive Thread | "Archive" action | Thread participant | Confirmation: "Archive this conversation? It will be hidden from your active list but preserved." | Yes |
| Close Thread | "Close" action | Admin | Confirmation: "Close this conversation? No further messages can be sent until reopened." On confirm: sets status = Closed. | Yes |
| Reopen Thread | "Reopen" action (Closed only) | Admin | Sets status = Open. | No |
| Delete Message | Delete icon on a message bubble | Admin only (not the author) | Destructive confirmation: "Delete this message? It will be removed for all participants. Audit trail preserved in Communication Logs." | Destructive |
| Delete Thread | "Delete Thread" action | Admin | Destructive confirmation with typed "DELETE" confirmation: "Delete this thread and all [N] messages? This cannot be undone." | Destructive (typed) |
| Export Thread | "Export" action | Admin | Downloads thread as PDF or markdown for records/compliance | No |
6.9 States
| State | Display |
|---|---|
| No threads | "No conversations yet." + "New Message" button (admin) / "Your inbox is empty." (TA) |
| Has threads — none selected | Conversation list populated; middle pane: "Select a conversation to view." |
| Thread selected | Full three-pane view |
| Thread closed | Middle pane shows messages with banner at bottom: "This conversation is closed. [Reopen]" (admin) / no composer (TA/student) |
| Thread archived | Does not appear in default "All" view; accessible via Archived filter |
| Loading thread | Skeleton message bubbles |
| Error sending | Composer shows inline error: "Message failed to send. [Retry]" |
| Unread messages | Thread card shows unread badge in conversation list; new messages in thread view marked with a subtle left accent until scrolled |
6.10 Role Visibility
- Admin: View all threads across all students/TAs; send as self; pre-draft for TAs; archive, close, reopen, delete threads/messages; export
- View-Only: View all threads (read-only). No send, no draft, no archive/close/delete. Useful for supervisors/auditors.
- TA: View only threads they participate in (own conversations with their students) + pre-drafts targeted to them (production-only queue). Send and reply in own threads. Cannot see other TAs' threads.
- Support Staff: View all threads (read-only) to support student inquiries. No send, no draft, no moderation actions.
- Students: Not a backend role — but their app-side experience pairs with this screen: they see threads initiated by admin or their TA, can reply, cannot initiate new threads against arbitrary admins (routing rules TBD, OQ-C9).
7. Screen: Communication Logs (NEW)
7.1 Purpose
Unified audit trail of every message sent from the backend, across all five outbound channels (automation email, blast email, push notification, announcement, private message). Supports filtering, full-message viewing, delivery-status investigation, and CSV export.
Distinct from Reporting > Logs (09-reporting.md §6), which covers system events (logins, record changes, admin actions). Communication Logs is communication-specific.
7.2 Entry Points
- Sidebar: Communication > Communication Logs
- Automation Emails > Template row > "View Send History" link
- Blast Emails > Campaign detail > "View Delivery" link
- Push Notifications > History row > "View Details" link
- Private Messages > Thread > "View in Logs" action (admin only)
- Student Detail > [future tab] > "Communication History" (cross-domain integration)
7.3 Layout
Standard filter bar + data table + pagination, following 01-global-patterns.md §4.1 and §4.2.
7.4 Filter Bar
| Filter | Type | Options |
|---|---|---|
| Search | Text input | Searches subject/preview and recipient names/emails |
| Channel | Dropdown (multi-select) | All / Automation Email / Blast Email / Push Notification / Announcement / Private Message |
| Sender | Dropdown (autocomplete) | Filters by sender_user_id — any admin or TA |
| Recipient | Dropdown (autocomplete) | Filters by any recipient_user_ids_json containing the selected user |
| Status | Dropdown | All / Queued / Sent / Delivered / Failed / Read |
| Pre-drafted | Toggle | Show only messages that were pre-drafted for a TA |
| Date Range | Date range picker | Sent-at range |
7.5 Data Displayed
| Column | Source | Sortable | Filterable | Notes |
|---|---|---|---|---|
| Timestamp | communication_logs.sent_at or created_at if unsent |
Yes (default: descending) | Yes (date range) | "Mar 8, 2026 2:14 PM" |
| Channel | communication_logs.channel |
Yes | Yes | Badge: Automation Email / Blast Email / Push Notification / Announcement / Private Message |
| Sender | users.name via sender_user_id |
Yes | Yes (dropdown) | The actual sender — per ADR-004, single identity. If predrafted = true, shows small "(pre-drafted)" tag. |
| Recipient(s) | users.name via recipient_user_ids_json |
No | Yes (dropdown) | For 1 recipient: name. For many: "[first name] + [N-1] more" with click to expand full list. |
| Subject/Preview | communication_logs.subject_or_preview |
Yes | Yes (search) | For emails: subject. For push: first 80 chars of message. For announcements: title. For private messages: first 80 chars of body. Truncated with tooltip. |
| Status | communication_logs.status |
Yes | Yes | Badge: Queued (gray) / Sent (green) / Delivered (green) / Failed (red) / Read (green) |
| Pre-drafted | communication_logs.predrafted + predraft_ta_user_id |
Yes | Yes (toggle) | Yes badge with TA name, or "—" |
| Actions | — | No | No | View / Retry (failed only, admin) / Open Source (jumps to origin screen) |
7.6 Actions
| Action | Trigger | Permission | Behavior | Confirmation |
|---|---|---|---|---|
| View Full Message | Row click or View icon | All roles with access | Opens detail modal: full sender/recipient list, rendered message body (as it was sent), delivery timeline (queued → sent → delivered → read if applicable), failure reason if Failed | No |
| Retry | "Retry" icon (Failed only) | Admin | Confirmation: "Retry sending this [channel] message to [N] recipients?" On confirm: re-queues via the original channel. Creates a new log entry; marks original as Retried. | Yes |
| Open Source | "Open Source" link in detail modal | All roles with access | Navigates to the originating screen: Automation Emails > Template detail, Blast Emails > Campaign detail, Push Notifications > History detail, Announcement Board > Post detail, or Private Messages > Thread | No |
| Export CSV | "Export" button (top-right) | Admin | Downloads CSV of current filtered/sorted view. Columns: timestamp, channel, sender, recipients (comma-separated), subject/preview, status, pre-drafted flag, failure reason. Includes up to 10,000 rows; warns if more. | No |
7.7 Pagination
Standard pagination (01-global-patterns.md §4.1): 50 records per page (higher than default 25 because logs are expected to be high-volume), "1–50 of 3,472" format.
7.8 States
| State | Display |
|---|---|
| Has logs | Data table |
| Empty (unfiltered) | "No communication has been sent yet." (should not occur in production) |
| Filtered — no results | "No communication matches your filters." + "Clear filters" link |
| Loading | Skeleton data table |
| Error | "Unable to load communication logs. Please try again." + "Retry" button |
| Export in progress | Toast: "Preparing export... this may take a moment." |
| Export ready | Toast: "Export ready. [Download]" (if async) or browser download (if sync) |
7.9 Role Visibility
- Admin: Full view + all actions (View, Retry, Open Source, Export)
- View-Only: Full view + View Full Message + Open Source. No Retry, no Export.
- TA: View only their own sent messages (pre-drafted or self-sent). No Retry, no Export. Sidebar visibility open question (OQ-C10) — mockup hides from TA sidebar.
- Support Staff: Full view + View Full Message + Open Source + Export (Export is critical for support ticket evidence). No Retry.
8. Cross-Domain Integration Points
This section documents how Communication connects to other domains. Each integration is specified in detail in the target domain's spec document; this section provides a summary for reference.
8.1 Semester Management (04-semester-management.md)
| Link | Direction | Behavior |
|---|---|---|
| Semester Hub > Setup Checklist > "Configure Email Templates" | Out → Communication | Deep link to Automation Emails with ?semester=[id] filter applied. Preserves the v1 workflow after the Email Management absorption per ADR-003. |
| Semester Hub > Overview > "View All Emails" | Out → Communication | Link to Automation Emails filtered to this semester |
| Semester Hub > End Checklist items (per ADR-001) | Out → Communication | Automated end-of-semester announcements may be configured as End Checklist items, deep-linking into Announcement Board |
| Semester Management > Tags | In ← Tags | Tag values populate the Shared Recipient Filter (§1.5) Tags dimension on Blast Emails, Push Notifications, Announcement Board |
8.2 Student Management (03-student-management.md)
| Link | Direction | Behavior |
|---|---|---|
| Student Detail > Profile tab > "Message" action | Out → Communication | Opens Private Messages with a new thread pre-populated with this student as recipient |
| Students list > bulk "Email Selected" action | Out → Communication | Opens Blast Emails > Compose with the selected students pre-populated as manual recipients |
| Student Detail > [Communication History] (future) | In ← Communication Logs | Shows a student-scoped slice of Communication Logs for support context |
8.3 Teacher Management (07-teacher-management.md)
| Link | Direction | Behavior |
|---|---|---|
| Teacher Detail > Profile tab > "Pre-draft Message for [TA]" action | Out → Communication | Opens Private Messages > Compose with this TA locked as the pre-draft sender; admin fills in student recipient and body |
| TA Schedule > session details > "Notify Student" | Out → Communication | Push notification pre-filled with session context |
8.4 Billing & Payments (08-billing-payments.md)
| Link | Direction | Behavior |
|---|---|---|
| Payment Overview > "Send Reminder" action on failed payments | Out → Communication | Opens Blast Emails > Compose (or pre-draft for TA) with a failed-payment-reminder template and the affected student(s) pre-selected |
| Scholarships & Deferments > Expiring Scholarships > "Contact" action | Out → Communication | Opens Private Messages thread with the scholarship student |
8.5 Admin & System (10-admin-system.md)
| Link | Direction | Behavior |
|---|---|---|
| Issue Queue > Issue Detail > "Start Message" | Out → Communication | Opens Private Messages thread with the issue reporter as recipient. Boundary: Issue Queue remains in Admin & System per ADR-003; messaging is a follow-up channel, not a replacement for the ticketing flow. |
| Admin & System > Notifications | — | Removed per ADR-003. Dashboard "Send Notification" quick action now targets Communication > Push Notifications. |
| Settings > Notifications | — | Removed per ADR-003. |
8.6 Dashboard (02-dashboard.md)
| Dashboard Element | Target |
|---|---|
| "Send Notification" quick action | Communication > Push Notifications (retargeted per ADR-003) |
| "Post Announcement" quick action (NEW) | Communication > Announcement Board |
| "Send Blast Email" quick action (NEW) | Communication > Blast Emails > Compose |
| Unread private messages tile (NEW — optional) | Communication > Private Messages, filtered to Unread |
8.7 Reporting (09-reporting.md)
| Link | Direction | Behavior |
|---|---|---|
| Reporting > Logs | Separate | System-level audit logs — does NOT duplicate Communication Logs. The two coexist: communication events are captured in Communication Logs; non-communication events (logins, record changes, admin actions) remain in Reporting > Logs. |
9. Assumptions and Open Questions
This domain is net-new and introduces several decisions that require stakeholder validation. Summary of communication-specific open questions, consolidated from ADR-003, ADR-004, and Framework v2 §3.6:
| ID | Question / Assumption | Impact on This Spec | Resolution Needed |
|---|---|---|---|
| OQ-C1 | Sequence View (§2.5): data source — only searched student, or sample student when none searched? | Mockup defaults to searched student. If sample needed, add a "Sample student" toggle above the search box. | Confirm with stakeholder: is "see what a typical student receives" a common need? |
| OQ-C2 | Template library: unified across Automation + Blast, or separate? | Resolved 2026-04-21 per STAKEHOLDER-ANSWERS (default #4) — Unified template library confirmed. Templates tagged by source_domain are reusable across Automation + Blast contexts. |
Resolved. |
| OQ-C3 | Scheduling: send-now only, or future scheduling for Blast Emails / Push Notifications / Announcements? | Resolved 2026-04-21 per STAKEHOLDER-ANSWERS (default #5) — Mockup supports send-now + save-draft only. Scheduling is deferred to production. Announcement Board continues to use expires_at for post expiry (distinct from scheduled publishing). |
Resolved. |
| OQ-C4 | Saved audiences: shared across admins or private per admin? | Mockup assumes shared (all admins see the same named audiences). | Confirm: are audiences a shared org resource or personal shortcuts? |
| OQ-C5 | Announcement read receipts: enabled by default, opt-in per post, or not supported at all? | Mockup treats as opt-in per post (read_receipt_enabled flag). |
Confirm: is this privacy-concerning or desirable metric? |
| OQ-C6 | Announcement Board: can TAs post, or admin-only? | Resolved 2026-04-21 per STAKEHOLDER-ANSWERS (default #6, CHANGED) — Admins AND TAs can post to Announcement Board. TAs manage their own posts (edit, delete, set audience, set expiry). Moderation of others' posts is admin-only. | Resolved. |
| OQ-C7 | Private Messages: multi-party threads (group conversations), or strictly 1:1? | Mockup assumes 1:1 (one student + one teacher/admin). "Add Participant" action placeholder included but may be removed. | Confirm: is group messaging a requirement? |
| OQ-C8 | Private Messages: attachment support scope — file types, size limits, storage? | Mockup shows UI affordance only. Production scope: images, PDFs, docs. Size limit TBD. | Define file-type whitelist, size limit, storage strategy (S3? local?). |
| OQ-C9 | Private Messages: who can students initiate new threads with? | Assumption: only their assigned TA and admins (support); not arbitrary TAs. Routing rules TBD. | Confirm routing and permission rules from student side. |
| OQ-C10 | Communication Logs: TA sidebar visibility — should TAs see a scoped Logs view of their own sends? | Mockup hides from TA sidebar entirely (TAs see sent items in their own production-only outbox). | Confirm: do TAs need a self-audit view? |
| OQ-C11 | Pre-draft expiry (ADR-004 OQ3): do unactioned pre-drafts expire, or persist indefinitely? | Resolved 2026-04-21 per STAKEHOLDER-ANSWERS (default #10) — Pre-drafts persist indefinitely. No expiry in v2. | Resolved. |
| OQ-C12 | Pre-draft multi-TA (ADR-004 OQ2): can an admin pre-draft one message for multiple TAs at once (each TA gets a personalized copy)? | Resolved 2026-04-21 per STAKEHOLDER-ANSWERS (default #9) — Single TA per draft. Multi-TA batched pre-drafting is not in v2 scope. | Resolved. |
| OQ-C13 | Pre-draft channels (ADR-004 OQ1): which channels support pre-drafting? | Resolved 2026-04-21 per STAKEHOLDER-ANSWERS (default #8) — Private Messages and Blast Emails support pre-drafting; Push Notifications and Announcement Board do not. | Resolved. |
| OQ-C14 | Automation Emails: do automations reference dynamic fields (e.g., TA name at send time) or frozen at template creation? | Mockup: dynamic per-send substitution. | Confirm: is this an explicit requirement? |
| OQ-C15 | Email sending infrastructure: existing transactional email provider (Postmark/SendGrid/SES)? | Assumed: existing infrastructure — mockup does not simulate actual send. | Confirm infrastructure; impacts Communication Logs delivery-status granularity. |
| OQ-C16 | Announcement expiry vs. delete: do expired announcements remain visible in the app (e.g., archive view) or disappear entirely? | Mockup: disappear from active view, remain in admin history. | Confirm user-side behavior. |
Preserved assumptions from absorbed domains:
emailstable semester-scoping is preserved exactly — no data migration required from v1 Email Management.push_notificationstable schema is extended (not replaced) — v1 records remain valid; new fields (recipient_filter_json, delivered/failed counts,predraft_for_ta_id) default to null for legacy rows.