Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

04 — Information Architecture (CRM Module)

Screen tree, navigation, and data entities for the CRM client. Global navigation conventions: 00-shared/05_Global_Information_Architecture.md. Routes shown as /crm/... under the authenticated shell.


1. Navigation model

The CRM is one top-level destination with three tabs and one sub-flow:

CRM (/crm)
├── Leads tab          /crm/leads
│   ├── Lead detail    /crm/leads/:id
│   │   ├── Lead editor (sheet)            /crm/leads/:id/edit   (S3)
│   │   ├── Follow-up composer (sheet)     (S2 inline)
│   │   └── Convert flow (dialog + states) (S4)
│   └── New lead       /crm/leads/new       (S3)
├── Admissions tab     /crm/admissions
│   ├── Admission detail /crm/admissions/:id
│   │   ├── Documents upload (sheet)       (S6 inline)
│   │   ├── Interview scheduler (sheet)    (S8)
│   │   └── Decision dialog                (S9)
│   └── New admission  /crm/admissions/new (S7)
└── Campaigns tab      /crm/campaigns
    └── New campaign   /crm/campaigns/new  (S11)
  • Master-detail: on tablets the tab list and detail render side-by-side; on phones detail is a pushed route.
  • Permission-gated nodes: writes (crm.lead.manage, crm.campaign.manage) hide FABs/actions; reads need crm.read (permissions.constants.ts:34-36). Enforcement on server is (planned) — client gates anyway.

2. Information hierarchy — Lead

Lead
├── Identity: firstName, middleName, lastName, email, phone
├── Pipeline: status (new/contacted/qualified/converted/closed)
│             closedAt, closedReason, convertedAt, convertedToStudentId
├── Source: source (website/referral/walk_in/phone/campaign/other)
├── Placement: gradeId, sectionId, academicYearId, classId
├── Ownership: assignedTo (Staff ref), notes
├── Follow-ups: [{note, scheduledAt, completedAt?, createdBy, createdAt}]  (timeline)
└── metadata (opaque)

Fields per lead.schema.ts:41-99; BaseSchema adds tenantId/createdAt/updatedAt/version.

Derived values (client-computed, no dedicated field): next follow-up due = earliest followUps[].scheduledAt without completedAt; "overdue" if due < now. Gap: no nextContactDate column exists — see 08_Form_Specifications.md §5.

3. Information hierarchy — Admission

Admission
├── Identity: firstName, middleName, lastName, email, phone
├── Pipeline: status (9 states, admission.schema.ts:7-17), submittedAt, decidedAt
├── Placement: gradeId, academicYearId, classId
├── Documents: [{type, fileId, filename, uploadedBy, uploadedAt}]
├── Interview: {scheduledAt, panel[], mode, feedback}
├── Workflow:  [{from, to, approver?, comment?, at}]   ← the audit trail
├── Conversion: {studentId, convertedAt}
└── notes

4. Information hierarchy — Campaign

Campaign
├── name, description
├── type (email/sms/social/print/other), status (draft/active/paused/completed)
├── startDate, endDate
├── targetAudience (opaque)
└── metrics {leadsGenerated, converted, sent, opened, clicked}  ← schema-only (planned write path)

5. Cross-entity relations

RelationEdgeSource
Lead → StudentconvertedToStudentId after convertlead.schema.ts:88-89
Lead → StaffassignedTolead.schema.ts:79-80
Lead → AcademicgradeId/sectionId/academicYearId/classId refslead.schema.ts:64-74
Lead → Campaignimplicit via source: campaign (no FK)lead.schema.ts:58-59
Admission → Studentconversion.studentId after convertadmission.schema.ts:83-89
Admission → Staffinterview.panel[], workflow[].approveradmission.schema.ts:59-60,73-74

Client note: lead/admission payloads carry raw ObjectIds for refs; detail screens resolve display names via the relevant module (students, staff, academics) — cross-module GETs are (planned) where endpoints are missing.

6. Filter & sort contract

ListServer filtersServer sort (fixed)Source
Leadsstatus only (page/limit)createdAt: -1crm.controller.ts:37-45; lead.repository.ts:31
Admissionsstatus only (page/limit)submittedAt: -1crm.controller.ts:103-111; admission.repository.ts:29
Campaignsnone (page/limit)createdAt: -1crm.controller.ts:77-81; campaign.repository.ts:25
  • Gaps: no assignedTo, source, q (search), or sort client param on any CRM list. Client filters (my leads, source chip) must be client-side over one page or wait for (planned) server filters. Global search covers leads (planned) (IMPLEMENTATION_PLAN.md §3).
  • Pagination meta: {page, limit, totalItems, totalPages, hasNext, hasPrevious} (00-shared/07 §2; buildPaginationMeta).

7. Labels & status vocabulary (single source)

  • Lead statuses render as: New / Contacted / Qualified / Converted / Closed ("Closed" = dropped in CRM terms).
  • Sources: Website / Referral / Walk-in / Phone / Campaign / Other.
  • Admission statuses: Draft / Submitted / Documents pending / Interview scheduled / Under review / Approved / Rejected / Waitlisted / Converted.
  • Decision actions: Approve / Reject / Waitlist (admission-decision.dto.ts:4-8).