Skip to content

Add LearnersReport component with learner table and empty states#14497

Draft
AllanOXDi wants to merge 8 commits intolearningequality:release-v0.19.xfrom
AllanOXDi:courses/14167/report-tab
Draft

Add LearnersReport component with learner table and empty states#14497
AllanOXDi wants to merge 8 commits intolearningequality:release-v0.19.xfrom
AllanOXDi:courses/14167/report-tab

Conversation

@AllanOXDi
Copy link
Copy Markdown
Member

@AllanOXDi AllanOXDi commented Mar 31, 2026

Summary

References

Reviewer guidance

AI usage

@github-actions github-actions bot added APP: Coach Re: Coach App (lessons, quizzes, groups, reports, etc.) DEV: frontend SIZE: very large labels Mar 31, 2026
@AllanOXDi AllanOXDi force-pushed the courses/14167/report-tab branch from b0cd765 to a9ff944 Compare March 31, 2026 22:28
@AllanOXDi AllanOXDi force-pushed the courses/14167/report-tab branch from a9ff944 to 52429d6 Compare March 31, 2026 22:30
@AllanOXDi AllanOXDi requested a review from rtibblesbot March 31, 2026 22:30
Copy link
Copy Markdown
Contributor

@rtibblesbot rtibblesbot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New LearnersReport table, LearnerSidePanel, and supporting i18n strings for per-learner score detail with risk-level badges.

CI passing. No linked issues; PR description is empty (no summary, screenshots, or AI usage disclosure). PR targets release-v0.19.x — not the default branch (develop) — and adds ~20 new i18n strings; see blocking note below.

  • blocking: Two i18n composition issues where numbers are rendered outside translated strings, breaking locale-aware formatting and translator word-order control (see inline comments)
  • blocking: New translatable strings added on a non-default branch (release-v0.19.x vs develop)
  • suggestion: PR description is entirely template placeholders — no summary, screenshots, or AI usage disclosure
  • suggestion: ::v-deep thead override in LearningObjectivesReport couples to KTable internals
  • praise: Thorough test coverage with boundary-value tests for risk-level thresholds and LO struggling bands
  • praise: Good a11y: aria-label on LO score spans, aria-hidden on decorative elements, and keyboard-navigable learner links

Phase 3 (UI verification) skipped — no screenshots in PR body and no dev server available.


@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly

How was this generated?

Reviewed the pull request diff checking for:

  • Correctness: bugs, edge cases, undocumented behavior, resource leaks, hardcoded values
  • Design: unnecessary complexity, naming, readability, comment accuracy, redundant state
  • Architecture: duplicated concerns, minimal interfaces, composition over inheritance
  • Testing: behavior-based assertions, mocks only at hard boundaries, accurate coverage
  • Completeness: missing dependencies, unupdated usages, i18n, accessibility, security
  • Principles: DRY (same reason to change), SRP, Rule of Three (no premature abstraction)
  • Checked CI status and linked issue acceptance criteria
  • For UI changes: inspected screenshots for layout, visual completeness, and consistency

class="stats-value"
:style="{ color: $themeTokens.annotation }"
>
<strong>{{ loCompletedCount }} {{ losCompletedOfLabel$({ total: loTotalCount }) }}</strong>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocking: loCompletedCount is rendered outside the translated string, which prevents locale-aware number formatting (digit grouping, decimals) and blocks translators from controlling word order.

The existing project pattern puts numbers inside i18n strings — e.g., nOfMLearners$({ n, m }) uses '{n, number} of {m, number} learners'. This should follow the same approach.

Consider combining the count into a single string:

losCompletedLabel: {
  message: '{completed, number} of {total, number} LOs completed',
  context: '...'
}

Then use <strong>{{ losCompletedLabel$({ completed: loCompletedCount, total: loTotalCount }) }}</strong> — the bold wraps the whole phrase rather than just the number, but i18n correctness takes priority over partial styling.

class="warning-icon"
/>
{{ strugglingWithObjectivesPrefixLabel$() }}
<b>{{ strugglingCount }} {{ strugglingWithObjectivesSuffixLabel$({ count: strugglingCount }) }}</b>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocking: The "struggling" warning composes a sentence from three separate elements: a prefix string, a raw number, and a suffix string. This prevents translators from reordering the sentence (some languages place the number after the noun, or restructure the phrase entirely).

The strugglingCount between the prefix and suffix isn't passed through ICU {count, number} formatting, so it also won't get locale-aware digit grouping.

Consider merging the prefix, count, and suffix into a single ICU string:

strugglingWithObjectivesLabel: {
  message: 'Learner is struggling with {count, number} {count, plural, one {learning objective} other {learning objectives}}',
  context: '...'
}

This loses the <b> on the count, but you could wrap the whole phrase in <b> or use a <span> with font-weight: bold on the container if emphasis is needed.

supportNeededLabel: {
message: 'Support needed',
context:
'Risk level badge shown in the Learners report for learners whose aggregate score is ≤45%',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocking: This PR adds ~20 new translatable strings but targets release-v0.19.x, not the default branch (develop). New strings on non-default branches typically cause problems because the i18n extraction and translation pipeline runs against the default branch.

Is this intentional — is the courses feature being developed exclusively on the release branch? If so, how will these strings get picked up for translation?


/* Visually hide KTable headers while keeping them accessible to screen readers */
.lo-report-table thead {
.lo-report-table ::v-deep thead {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: ::v-deep thead reaches into KTable's internal DOM to visually hide headers. This is fragile — if KTable changes its markup (e.g., wraps <thead> or uses a different element), this breaks silently.

Does KTable offer a prop to hide column headers while keeping them accessible? If not, this approach works, but a comment noting the coupling would help future maintainers.

<span class="lo-text">{{ lo.text }}</span>
<span
class="lo-score"
:aria-label="xOfYCorrectLabel$({ correct: lo.correct, total: lo.numQuestions })"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: Good accessibility — aria-label on score spans provides a complete "X of Y correct" reading for screen readers, while aria-hidden on the visual-only number and "of N" text prevents double-reading. Well done.

expect(screen.getByText(onTrackLabel$())).toBeInTheDocument();
});

it('shows "Support needed" (red) for learner at exactly 45%', () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: Thorough boundary-value testing — the 45% exact boundary (lines 163–182) and 60%+ boundary (lines 184–189) tests catch off-by-one errors in the risk-level thresholds. The LearnerSidePanel tests similarly test the 80% struggling boundary. This is exactly the kind of edge-case coverage that prevents subtle regressions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

APP: Coach Re: Coach App (lessons, quizzes, groups, reports, etc.) DEV: frontend SIZE: very large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants