Context
The faculty analysis view currently shows quantitative performance via (a) a bar-style per-section performance chart and (b) a per-question table nested under each section. Both are aggregated at the section level (weighted average of section questions).
However, the underlying data model also tracks dimension codes on QuestionnaireAnswer (e.g. PREPARATION, TEACHING_LEARNING_PROCESS, ASSESSMENT, LEARNING_ENVIRONMENT, PROFESSIONALISM) which cut orthogonally to sections. Dimensions are a richer diagnostic view because:
- Fewer, more meaningful categories (typically 4–7) — sweet spot for radar visualisation
- Map cleanly to well-known pedagogical dimensions stakeholders recognise
- Already used by
RecommendationGenerationService.Generate (lines ~172-197) to bias LLM recommendations
- Produce an at-a-glance "fingerprint" of faculty strengths and weaknesses
Proposed feature
Add a radar (spider) chart visualisation of per-dimension averages, mounted at the top of the Quantitative Scores collapsible in FacultyReportScreen. Existing section chart stays — the two are complementary (dimensions = cross-section, sections = weight-aggregated).

Backend
- Extract the dimension-aggregation logic currently inline in
RecommendationGenerationService.Generate into a reusable helper on AnalyticsService (or a shared lib under src/modules/analytics/lib/).
- Extend
GET /analytics/faculty/:facultyId/report response (or add a sibling endpoint) to include:
```ts
dimensionScores: {
code: string; // e.g. 'PREPARATION'
label: string; // human-readable from dimension registry
average: number; // normalised 0–1
responseCount: number; // questions × submissions contributing
}[]
```
- Normalise to 0–1 so different Likert ranges (
LIKERT_1_4 vs LIKERT_1_5) plot comparably. Divide by the per-question max so the radar axis is always 0–100% full.
Frontend
- New
faculty-dimension-radar-chart.tsx component using recharts' RadarChart (recharts already installed for the existing section chart — no new deps).
- Mount at the top of the Quantitative Scores collapsible, above the existing section performance chart.
- Theme-token styled (stroke / fill using
var(--color-primary) + opacity, grid lines via var(--color-border)). Dark-mode aware.
- Responsive sizing via recharts'
ResponsiveContainer.
Stretch — comparative overlay
- Second radar layered over the faculty's own, representing department average for the same semester + questionnaire type (dashed line, muted colour).
- Requires a new backend endpoint or param:
GET /analytics/department/:deptId/dimensions?semesterId=&questionnaireTypeCode=.
- Unlocks "how does this faculty differ from their peers" at a glance — the single biggest stakeholder value-add.
Concerns to resolve during refinement
- Dimension taxonomy consistency — does every questionnaire type use the same set of dimension codes? If not, the radar is per-questionnaire-type and we need to confirm per-type dimension metadata (label, display order) exists.
- Scale normalisation — confirm all answer types can be normalised to 0–1;
LIKERT_1_4, LIKERT_1_5 are obvious; investigate whether any non-likert answer types contribute to dimension_code aggregation and how they normalise.
- Minimum responses per dimension — show the radar only if every dimension has >= N responses? Or dim the sparse spokes? Tie-in with the low-volume-handling companion ticket.
Acceptance criteria
Related
- FAC-134 (quantitative section UX — this lands inside the collapsible we built there)
- FAC-138 (recommendation prompt — the radar's "weakest dimension" could explicitly bias the LLM prompt)
- FAC-132 (pipeline — where the aggregation logic currently lives, to be extracted)
- Future: dashboard-scope extension — same radar at department / program / campus scope on the scoped-analytics-dashboard
Context
The faculty analysis view currently shows quantitative performance via (a) a bar-style per-section performance chart and (b) a per-question table nested under each section. Both are aggregated at the section level (weighted average of section questions).
However, the underlying data model also tracks dimension codes on
QuestionnaireAnswer(e.g.PREPARATION,TEACHING_LEARNING_PROCESS,ASSESSMENT,LEARNING_ENVIRONMENT,PROFESSIONALISM) which cut orthogonally to sections. Dimensions are a richer diagnostic view because:RecommendationGenerationService.Generate(lines ~172-197) to bias LLM recommendationsProposed feature
Add a radar (spider) chart visualisation of per-dimension averages, mounted at the top of the Quantitative Scores collapsible in
FacultyReportScreen. Existing section chart stays — the two are complementary (dimensions = cross-section, sections = weight-aggregated).Backend
RecommendationGenerationService.Generateinto a reusable helper onAnalyticsService(or a shared lib undersrc/modules/analytics/lib/).GET /analytics/faculty/:facultyId/reportresponse (or add a sibling endpoint) to include:```ts
dimensionScores: {
code: string; // e.g. 'PREPARATION'
label: string; // human-readable from dimension registry
average: number; // normalised 0–1
responseCount: number; // questions × submissions contributing
}[]
```
LIKERT_1_4vsLIKERT_1_5) plot comparably. Divide by the per-question max so the radar axis is always 0–100% full.Frontend
faculty-dimension-radar-chart.tsxcomponent using recharts'RadarChart(recharts already installed for the existing section chart — no new deps).var(--color-primary)+ opacity, grid lines viavar(--color-border)). Dark-mode aware.ResponsiveContainer.Stretch — comparative overlay
GET /analytics/department/:deptId/dimensions?semesterId=&questionnaireTypeCode=.Concerns to resolve during refinement
LIKERT_1_4,LIKERT_1_5are obvious; investigate whether any non-likert answer types contribute todimension_codeaggregation and how they normalise.Acceptance criteria
dimensionScores[]with normalised 0–1 averages and response countsRecommendationGenerationServiceinto a shared helper (eliminates duplication)Related