Skip to content

Add unit page#14614

Open
marcellamaki wants to merge 5 commits into
learningequality:developfrom
marcellamaki:add-unit-page
Open

Add unit page#14614
marcellamaki wants to merge 5 commits into
learningequality:developfrom
marcellamaki:add-unit-page

Conversation

@marcellamaki
Copy link
Copy Markdown
Member

@marcellamaki marcellamaki commented Apr 16, 2026

Summary

Adds a Unit Detail page accessible via the UNITS tab on the course summary page.

2 tab interface (closer to figma spec, deviation from technical spec, because I realized upon implementation that we'd lost the learner resource progress with my suggestion, which seems really unhelpful for coaches)

References

Fixes #14471

Screen.Recording.2026-04-16.at.2.30.06.PM.mov

Reviewer guidance

  • Navigate to a course session → unit → confirm you land on the unit detail page with back link, unit title,
    and tabs
    • Switch to Learning Objectives tab → refresh → confirm you stay on Learning Objectives tab
    • Confirm back link returns to the correct course summary page
    • Test on a unit with no pre/post test activated: Learning Objectives tab should show learning objective names listed with empty sparklines (no test data), not an error state
    • Mobile responsiveness should be managed on both tabs

AI usage

written with claude with multiple rounds of claude and self-review and careful prompting

@github-actions github-actions Bot added DEV: backend Python, databases, networking, filesystem... APP: Coach Re: Coach App (lessons, quizzes, groups, reports, etc.) DEV: frontend SIZE: large labels Apr 16, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 16, 2026

@marcellamaki marcellamaki force-pushed the add-unit-page branch 2 times, most recently from 293e15b to a4736a7 Compare April 16, 2026 17:02
@marcellamaki marcellamaki marked this pull request as ready for review April 16, 2026 18:31
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.

Solid implementation overall — the data layer is well-structured and the backend tests are thorough. CI all green.

The PR intentionally deviates from two acceptance criteria in #14471: (1) tabs instead of "No tabs", and (2) per-resource StatusSummary inside the accordion instead of per-lesson aggregate in the header. The tab deviation is explained in the PR description and matches the Figma; the header deviation is not mentioned. Please confirm both are intentional so reviewers can approve the spec change explicitly rather than silently.

Visual inspection from the video: the unit page renders correctly, tab switching works, accordion expand/collapse functions, SparklineBars display on the Learning Objectives tab, and the back link is correct.

Suggestions:

  • Accordions open by default (see inline on UnitDetailPage.vue:39)
  • StatusSummary in accordion header omitted (see inline on UnitDetailPage.vue)
  • Silent tally corruption on unknown status (see inline on useUnitDetail.js:57)
  • H5P/ZIM scope concern (see inline on ContentIcon.vue:55)
  • New strings (backToCourseLabel, learnerDistributionChartLabel) are on release-v0.19.x rather than develop — confirm these will be included in the translation pipeline for this release.

Nitpick: useUnitDetail.spec.js — the "no active test" fallback path in objectivesForLesson (composable lines 138–148) isn't covered by the unit tests. A test that sets rawLearningObjectives with data and bucketedObjectives empty would close that gap.

Praise:

  • useUnitDetail.js — excellent O(n) contentStatusIndex preprocessing
  • test_unit_lesson_progress.py — comprehensive backend tests: permission levels, HelpNeeded superseded by completion, learner group scoping, empty unit edge case
  • Tab persistence via URL query params with TAB_VALUES validation is clean and bookmark-safe
  • objectivesForLesson fallback to rawLearningObjectives when no test data exists is thoughtful

Accordion header deviation (not inline due to diff position):
UnitDetailPage.vue <template #title> (lines 42–50 and 107–115): The acceptance criterion says "Each lesson accordion header shows StatusSummary with aggregate learner completion progress", but the header only shows title + icon. The per-resource StatusSummary is inside the accordion content. This provides more granular data but deviates from the stated spec — worth calling out for explicit sign-off.


@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

<AccordionItem
v-for="lesson in lessons"
:key="lesson.id"
isOpenByDefault
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: isOpenByDefault opens all lesson accordions on initial load. The acceptance criterion in #14471 says "all collapsed by default". If this is intentional (showing content immediately may be better UX), please note the deviation explicitly — otherwise remove isOpenByDefault to match the spec. The same applies to line 103 in the Learning Objectives tab.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think my only concern here would be "what happens when there are too many things?"

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.

That's a valid UX tradeoff — if there are many lessons, auto-expanding everything could be overwhelming. The right call here is a product decision between matching the spec (collapsed) vs. immediate visibility. Happy to defer to whatever you and the team decide.

total: learnerIds.length,
};
for (const learnerId of learnerIds) {
const status = byLearner[learnerId] || STATUS_NOT_STARTED;
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: If status ever holds a value not in STATUS_KEY_MAP (e.g., a new status added to the backend without a frontend update), STATUS_KEY_MAP[status] is undefined and tally[undefined] += 1 silently corrupts the count. Consider a safe fallback:

tally[STATUS_KEY_MAP[status] ?? 'notStarted'] += 1;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This seems like a code error that we should be covering elsewhere.

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.

Fair point — if there's a backend/frontend version skew that produces an unknown status, that's better caught at the data layer or with an explicit assertion, not silently swallowed here. Withdrawing this suggestion.


// Index status entries by content_id → learner_id so resourceTally is O(1) lookup
// rather than scanning all entries per resource per render.
const contentStatusIndex = computed(() => {
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: Preprocessing contentStatusIndex as a computed O(n) index rather than scanning all status entries per resource per render is the right call — avoids a quadratic cost when a lesson has many resources and many learners.

style="top: 0"
/>
<KIcon
v-if="is(ContentNodeKinds.H5P)"
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: The H5P and ZIM icon additions (plus ContentNodeKinds.H5P/ZIM in packages/kolibri/constants.js) look correct, but they affect the entire application rather than just the unit detail page. Were these intentionally bundled here, or should they be a separate PR targeting develop so all plugins benefit and the change gets a focused review?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This has been a long standing gap - as release will be merged up this, seems fine.

Copy link
Copy Markdown
Member

@rtibbles rtibbles left a comment

Choose a reason for hiding this comment

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

I don't think there's anything blocking per se - but this seems to be ignoring some of the things that have already been set in place by previous work in terms of conventions.

// These must match the PascalCase strings returned by class_summary_api.get_log_status
const STATUS_NOT_STARTED = 'NotStarted';

const STATUS_KEY_MAP = {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we not have something like this and the above defined somewhere else already that could be imported?

*/
export function computeResourceTally(contentId, contentStatusIndex, learnerIds) {
const byLearner = contentStatusIndex[contentId] || {};
const tally = {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this not duplicative of logic elsewhere in coach? If not, could also just iterate over the status map as the source of truth in order to populate the object with the status keys.

.catch(() => {
createSnackbar(defaultErrorMessage$());
})
.finally(() => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Have we confirmed that the finally polyfill works on Safari iOS 9.3? https://caniuse.com/promise-finally

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

both claude and google say that our corejs polyfill cover this. if you know otherwise, it looks like there are a few other places in the code where this lives, so i could fix here, but we should resolve elsewhere as well

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.

You're right — if corejs covers , then this is a non-issue. Withdrawing this finding.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hey, @rtibblesbot you don't need to withdraw my findings, thanks very much - you're not me!

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.

Apologies for the confusion — the original concern was yours, not mine. I shouldn't have said "Withdrawing this finding" since I have no standing to withdraw your comment. That was poorly worded on my part.

},
{
name: PageNames.UNIT_DETAIL,
path: CLASS + COURSE_SESSION + '/units/:unitContentnodeId',
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would love to see a COMPACT_UUID_PATTERN in here - validation ftw!


const STUBS = {
CoachAppBarPage: { template: '<div><slot /></div>' },
KRouterLink: { props: ['to', 'text'], template: '<a>{{ text }}</a>' },
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is quite heavy stubbing here - is there a particular reason for it?

},
];

const DEFAULT_COMPOSABLE = {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could we move this into a mocks folder alongside the composable, rather than defining it inline?

{
name: PageNames.UNIT_DETAIL,
path: CLASS + COURSE_SESSION + '/units/:unitContentnodeId',
component: UnitDetailPage,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No child routes for the tabs to have their own URLs? We've established the pattern in this file already, let's continue it.

});

function setActiveTab(id) {
router.replace({ query: { ...route.query, tab: id } });
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I have a query about this deviation from the already established navigation pattern!

context:
'In Kolibri, an app is a certain kind of learning resource. Specifically, apps are generally self-contained, interactive HTML and Javascript applications.',
},
h5p: {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could we avoid adding new keys here and just map to the existing keys instead?

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.

This delta re-review covers new findings only. UI verified via video walkthrough — tab switching, accordion expand/collapse, sparkline rendering, back link, and mobile layout all work correctly.

Prior findings

All 4 prior findings (3 suggestions acknowledged, 1 praise) — no new status changes since last review.


CI: "All file linting" is failing. The failing files in the pre-commit output appear to be outside this PR's scope (e.g., LessonActive.vue, auth-select-page.spec.js). However the base branch passed linting on April 20 — please rebase onto the latest release-v0.19.x and rerun, or confirm this is a pre-existing issue unblocking the PR.

  • blocking: CI All file linting check failing — see above. Failing files appear outside the diff; rebase and reinvestigate.
  • blocking: New i18n strings on non-default branch — see inline on commonCoachStrings.js:90.
  • praise: objectivesForLesson fallback — see inline on useUnitDetail.js:125.

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

How was this generated?

Compared the current PR state against findings from a prior review:

  • Retrieved prior bot reviews deterministically via the GitHub API
  • Classified each prior finding as RESOLVED, UNADDRESSED, ACKNOWLEDGED, or CONTESTED
  • Only raised NEW findings for newly introduced code
  • Reviewed the pull request diff checking for correctness, design, architecture, testing, completeness, and adherence to DRY/SRP principles
  • Checked CI status and linked issue acceptance criteria

message: "Back to '{lesson}'",
context: 'Link that takes the coach back to the lesson view.',
},
backToCourseLabel: {
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: backToCourseLabel (here) and learnerDistributionChartLabel (line 448) are new translatable strings added on release-v0.19.x rather than the default develop branch. New strings on release branches may not enter the translation pipeline before the release ships. Please confirm the project's translation workflow covers strings added directly to this branch — or, if the branch is always merged into develop before the release, note that explicitly so reviewers can confirm the strings will be processed.

return `${unitNLabel$({ num: unitNumber.value })} ${unitTitle.value}`;
});

function objectivesForLesson(lessonId) {
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: The fallback to zero-count raw LOs when bucketedObjectives is empty is good defensive coding — coaches see LO names even before a pre/post test is activated, so the accordion isn't blank. The two-source selection is cleanly bounded by the single condition and well-documented.

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.

Two blocking findings from the prior review remain open; the new ContentIcon commit is clean.

CI: All file linting still failing (see inline on UnitDetailPage.spec.js:39). Other checks passing or pending (build assets still in progress). Visual inspection from the PR video — unit page, tab switching, sparklines, back link, and Learning Objectives tab all render correctly; the new ContentIcon H5P/ZIM changes are low-risk and not visible in the video.

Prior findings

6 prior findings unchanged (4 acknowledged, 2 praise).

Still unaddressed (re-raised below):

  • CI All file linting failing (blocking) — no reply, no code change; note: failing file shifted from unrelated files to UnitDetailPage.spec.js in the PR
  • New i18n strings on release-v0.19.x (blocking) — no reply, strings still present

0/8 prior findings resolved. 2 re-raised below.


  • blocking: CI linting failure in UnitDetailPage.spec.js — see inline comment
  • blocking: New translatable strings on non-default branch — see inline on commonCoachStrings.js:90
  • praise: ContentIcon H5P/ZIM mapping — see inline

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

How was this generated?

Compared the current PR state against findings from a prior review:

  • Retrieved prior bot reviews deterministically via the GitHub API
  • Classified each prior finding as RESOLVED, UNADDRESSED, ACKNOWLEDGED, or CONTESTED
  • Only raised NEW findings for newly introduced code
  • Reviewed the pull request diff checking for correctness, design, architecture, testing, completeness, and adherence to DRY/SRP principles
  • Checked CI status and linked issue acceptance criteria

};

// Omit AccordionItem stub so the real component renders (collapsed by default).
const { AccordionItem: _unused, ...STUBS_REAL_ACCORDION } = STUBS;
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: CI All file linting is failing for this file (confirmed: run 24698373678). The base branch passes linting. The [pre-commit.ci lite] auto-fix only corrected Prettier indentation; the remaining ESLint error was not auto-fixable. The most likely cause is _unused here — ESLint's no-unused-vars (from eslint:recommended) does not automatically ignore _-prefixed variables unless ignoreRestSiblings: true or a varsIgnorePattern is configured, and neither appears in the project config. To confirm and fix: run pnpm run lint-frontend -- --files kolibri/plugins/coach/frontend/views/courses/__tests__/UnitDetailPage.spec.js. The pattern used elsewhere in the codebase for silencing this rule is an inline comment, e.g.:

// eslint-disable-next-line no-unused-vars
const { AccordionItem, ...STUBS_REAL_ACCORDION } = STUBS;

message: "Back to '{lesson}'",
context: 'Link that takes the coach back to the lesson view.',
},
backToCourseLabel: {
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 (re-raised): backToCourseLabel (here) and learnerDistributionChartLabel (line 448) are new translatable strings being added directly to release-v0.19.x rather than the default develop branch. New strings on release branches may not enter the translation pipeline before the release ships, leaving them untranslated for all non-English users. Please confirm one of the following:

  1. The translation workflow is configured to pick up strings from this branch before release, OR
  2. This branch is always merged into develop before the release ships (making the strings visible to the pipeline)

If neither applies, the strings should be added to develop instead.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ignore this please, this is intended.

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.

Understood — deferring to @rtibbles's confirmation that this is intentional. Withdrawing this finding. Apologies for the noise.

/>
<KIcon
v-if="is(ContentNodeKinds.H5P)"
:icon="ContentNodeKinds.HTML5"
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: Mapping H5P to the existing HTML5 icon token (and ZIM to DOCUMENT) rather than introducing new tokens is exactly right — it reuses the already-registered icon assets and avoids growing the icon registry. The kindToLabeLMap additions at lines 178–179 keep the tooltip reuse consistent.

@marcellamaki marcellamaki force-pushed the add-unit-page branch 2 times, most recently from de78a00 to 89592d4 Compare April 21, 2026 19:18
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.

No new commits since my last review — this covers only the outstanding blocking finding.

CI: All file linting still failing (inline comment below). All other checks passing.

Prior findings

3 prior suggestions/praise acknowledged and unchanged.

Newly acknowledged (not re-raised):

  • New strings on release-v0.19.x (blocking) ← @rtibbles confirmed: "this is intentional"

Still unaddressed (re-raised below):

  • CI All file linting failing at UnitDetailPage.spec.js:39 (blocking) — no reply, no code change

1/2 prior blocking findings acknowledged. 1 re-raised below.


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

How was this generated?

Compared the current PR state against findings from a prior review:

  • Retrieved prior bot reviews deterministically via the GitHub API
  • Classified each prior finding as RESOLVED, UNADDRESSED, ACKNOWLEDGED, or CONTESTED
  • Only raised NEW findings for newly introduced code
  • Reviewed the pull request diff checking for correctness, design, architecture, testing, completeness, and adherence to DRY/SRP principles
  • Checked CI status and linked issue acceptance criteria

};

// Omit AccordionItem stub so the real component renders (collapsed by default).
const { AccordionItem: _, ...STUBS_REAL_ACCORDION } = STUBS;
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 (re-raised): CI All file linting is still failing here (run 24741710321, error: 39:24 'no-unused-vars'). ESLint flags _ as assigned but never read. Add a disable comment:

// eslint-disable-next-line no-unused-vars
const { AccordionItem: _, ...STUBS_REAL_ACCORDION } = STUBS;

Or restructure to avoid capturing the omitted key:

const STUBS_REAL_ACCORDION = Object.fromEntries(
  Object.entries(STUBS).filter(([key]) => key !== 'AccordionItem')
);

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.

All blocking findings from prior rounds are resolved; approving.

CI: All checks passing except Python unit tests on Mac OS (3.10), which is failing because /api/auth/portal/validate_token/ returns a Cloudflare 521 — the origin service is down in this runner environment. The failure is unrelated to this PR's changes (auth portal validation is untouched) and is absent from the base branch's failure list when run at a different time. Not blocking.

UI verified via video walkthrough in an earlier review round. Visual inspection covered tab switching, accordion expand/collapse, sparklines, back link, and mobile layout.

Note: @rtibbles has five unanswered inline comments on this PR (stubbing rationale, __mocks__ folder for the mock, the navigation-pattern query on UnitDetailPage.vue, and two that appear to have been addressed by the current code — COMPACT_UUID_PATTERN is used and child routes for both tabs exist). These are @rtibbles' own open threads; flagging them here in case they need a reply before merge.

Prior findings

All prior non-blocking findings unchanged (acknowledged, not re-raised).

Newly resolved:

  • CI All file linting failing at UnitDetailPage.spec.js:39 (blocking) ← was UNADDRESSED — no-unused-vars is gone from the spec; linting passes in run 24744236891.

2/2 blocking findings now resolved. 0 re-raised.


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

How was this generated?

Compared the current PR state against findings from a prior review:

  • Retrieved prior bot reviews deterministically via the GitHub API
  • Classified each prior finding as RESOLVED, UNADDRESSED, ACKNOWLEDGED, or CONTESTED
  • Only raised NEW findings for newly introduced code
  • Reviewed the pull request diff checking for correctness, design, architecture, testing, completeness, and adherence to DRY/SRP principles
  • Checked CI status and linked issue acceptance criteria

Copy link
Copy Markdown
Member

@rtibbles rtibbles left a comment

Choose a reason for hiding this comment

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

A couple more questions about testing, but this seems ready for QA.

@@ -0,0 +1,67 @@
import {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Oh... why is this here, rather than in the tests folder in composables, adjacent to the composable itself?


const STUBS = {
// AccordionItem: always renders slots so tests can assert on content without expanding.
AccordionItem: { template: '<div><slot name="title" /><slot name="content" /></div>' },
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Still quite a few stubs, are these really necessary?


loading.value = true;

Promise.all([
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I guess the other alternative here is to try/catch with await - then finally just becomes something that happens after the try/catch block.

@pcenov
Copy link
Copy Markdown
Member

pcenov commented Apr 22, 2026

Hi @marcellamaki - could you please double check whether there is an issue with the build here as testing on kolibri_0.19.4.dev0+git.20260421201612-0ubuntu1_all.deb results in not seeing any data in 'Learning objectives', not seeing the Back link, constant loading etc:

unit.page.mp4

@marcellamaki
Copy link
Copy Markdown
Member Author

hmmm, yes thank you @pcenov , I will look into that and confirm. sorry for the issues with this!

@marcellamaki marcellamaki force-pushed the add-unit-page branch 3 times, most recently from 168fcc8 to 1e4ca7e Compare April 22, 2026 23:00
@marcellamaki
Copy link
Copy Markdown
Member Author

Hi @pcenov i wasn't able to reproduce the exact issue you saw on local dev or on the mac app, but I did see an error in the console (which didn't display to you) which could have caused the loading issues and resolved it so I'm hoping that fixes it 🤞

@pcenov
Copy link
Copy Markdown
Member

pcenov commented Apr 23, 2026

Hi @marcellamaki - not sure why you were not able to reproduce the previous issues... here's what I am observing in the current build which also seems to be not the same as what you are seeing locally but is better than the previous one:

  1. In the current build the Learning objectives tab doesn't get populated with data no matter what I do (tested on both Ubuntu and Mac, clean install of the app):
no.objectives.mp4
  1. For the Lessons tab I would expect at least the first lesson to be expanded by default because the collapsed accordion doesn't carry any useful info and I always find myself clicking to expand them (also would be nice to have an expand/collapse all option I guess):
expanded.mp4
  1. For the Learning objectives tab when a test has not been activated we should probably also be showing the text 'No test has been activated for this unit yet' and the first item should probably be expanded by default:
learning.objectives.mp4
  1. Initially I deleted all previously assigned courses and started with a brand new course, however when I go to Courses > Course > Units > Unit > Lessons I can see resources marked as 'Completed by 1 of 3'. If I go to the Learners and Learning objectives tab for the same course I see 'No progress yet' and 'No test has been activated for this unit yet' messages so it should be further clarified what's the expected behavior in that case:
previously.completed.mp4

@github-actions
Copy link
Copy Markdown
Contributor

npm Package Versions

Warning

The following packages have changed files but no version bump:

Package Version Changed files
kolibri 0.18.0 1

If these changes affect published code, consider bumping the version.

@marcellamaki
Copy link
Copy Markdown
Member Author

@pcenov - very belated, this is ready for re-QA. You will need to update the courses with the latest version from the kolibri QA channel, but hopefully the updates on that end should have resolved the data/loading problems you were seeing (I have rechecked locally the same way I was originally able to reproduce the error and it looks good to me, but, I guess we will know for sure once you try!)

I've also updated points 2 and 3. As for point 4, I am still thinking about this. I'm not sure what makes the most sense here, because this is somewhat of a new instance of a pre-existing problem (progress being tied to the resource, not the context). There may be some design/UX approaches for improving this, but I'm just not sure what they are yet.

@pcenov
Copy link
Copy Markdown
Member

pcenov commented May 20, 2026

Thanks @marcellamaki - I confirm that points 1-3 are fixed now and I have filed separately the following issues #14733 and #14734

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: backend Python, databases, networking, filesystem... DEV: frontend SIZE: large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add unit detail page with lesson accordions and learning objectives

5 participants