You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Blocked by:#669 (the accessible-courses-by-date endpoint + course outlines) — a TBD contract; wire against it and flag real paths as fill-in-later. The competency tree itself has no new backend dependency — it reuses the existing taxonomy tags list API (a competency is a Tag in a competency taxonomy; that endpoint already returns them).
Repo: frontend-app-authoring. MFE-only. This ticket is #670; the select/deselect-association UI is #672.
Use Case
As a Platform Administrator configuring Competency Criteria associations, I want to select a competency from the taxonomy's competency tree and then search the courses I have access to (filtered by course-run start date), browse a course's sections and gradeable subsections, so that I can find the exact gradeable subsection to attach criteria to without opening each course individually.
Description
Current state
No implementation exists. #623 built only a placeholder Competency Management page (/taxonomy/:taxonomyId/competencies) whose real content is deferred to future tickets; this is one. There is no left-hand competency tree in code yet: #648designs the shell and that tree, but no implementation ticket builds it, and this is the first ticket that needs a competency actually selected. Studio's existing StudioHome course listing is general and competency-unaware, with no date-range filter and no drill-down into a course's gradeable structure.
Requested change
Build the Manage & Apply Competencies associations workspace: a minimal two-panel layout with a competency-selection tree on the left and the course-browsing panel on the right, driven by the selected competency.
Minimal competency-selection tree (left panel). Render the current taxonomy's competencies as a selectable tree (nested competencies with their codes). Selecting a competency sets the active competency and populates/refreshes the right-hand panel against it. Reuse the existing tag-tree rendering; this is the minimal functional tree, not the finished shell. Expand/collapse polish, switcher styling, empty-state design, and responsive spec are [UXD] Build the UI for Competency Criteria Associations #648's; the Competency ID badge on rows is a separate ticket's. Neither is built here.
Course search with a start-date range filter (right panel). A title search plus a date-range picker, scoped to the selected competency. Use React Date Picker lightly styled with Paragon (Paragon has no native range picker). For MVP the range filters on course-run start date only; make that explicit to the user ("courses whose start date falls between X and Y"). Results come from [BE] Add start-date range filter and outline delivery for accessible courses (Competency Criteria Associations) #669 (paginated accessible courses).
The full Manage & Apply shell design and tree polish — the switcher styling, expand/collapse-all affordances, empty states, and responsive spec ([UXD] Build the UI for Competency Criteria Associations #648). This ticket builds only the minimal functional tree + selection needed to drive the right panel.
The Competency ID badge on tree rows (a separate ticket).
The "Programs" button / program "P" icon, and any course-level associate button — these were removed from the design; do not build them regardless of what a screenshot shows.
The "Demonstrate Mastery For" rule header and its controls, and "Import Competency Framework" — separate, unrelated tickets.
Acceptance Criteria
Frontend ticket.
Scenario: Competency tree lists the taxonomy's competencies
Given I open the Manage & Apply Competencies page for a competency taxonomy
Then the left panel shows that taxonomy's competencies as a nested tree
Scenario: Selecting a competency drives the right-hand panel
Given the competency tree is shown
When I select a competency
Then it becomes the active competency
And the right-hand course-browse panel loads for that competency
Scenario: No competency selected yet
Given I open the page and have not selected a competency
Then the right-hand panel prompts me to select a competency
And no course search runs until one is selected
Scenario: Search courses by title
Given I have selected a competency and am viewing the course-browse panel
When I enter a title filter
Then the course list narrows to accessible courses whose title matches
Scenario: Filter courses by start-date range
Given I am viewing the panel
When I set a start and end boundary in the date-range picker
Then the list shows only courses whose course-run start date falls within that range
And courses with no start date still appear
And the UI makes clear the filter is on course start date
Scenario: Default view shows all accessible courses
Given I have entered no title filter and no date range
When the panel loads
Then it shows all courses I have access to
Scenario: Expand a course to browse its gradeable subsections
Given a course appears in the results
When I expand it
Then its sections and gradeable subsections are displayed
And the course and section rows are navigation only, not selectable as association targets
Scenario: Course search fails
Given the #669 course-search call fails
Then an inline error state is shown, distinct from an empty-results state
Scenario: No matching courses
Given the search/filter matches no accessible course
Then an empty state is shown
Scenario: Small-screen rendering
Given I view the page on a small screen
Then the competency tree, search, date picker, and expand/collapse remain usable
Open Questions
[non-blocking, owner: architect] Boundary with the [UXD] Build the UI for Competency Criteria Associations #648 shell implementation.[UXD] Build the UI for Competency Criteria Associations #648 is being reframed as a parent UX-design ticket with implementation children, one of which builds the full shell + competency tree. If that shell-implementation child lands, this ticket's minimal tree should be absorbed/superseded by it, not duplicated. Until it exists, the minimal tree lives here. Confirm the intended sequencing so the tree is built once.
[BLOCKING, owner: BA] What was this ticket's "different confusion"? An earlier note flagged this ticket for redoing "due to a different confusion" without specifying what. This rewrite assumes it's the course-vs-subsection level correction (associate at gradeable-subsection level; courses/sections are navigation). Confirm before this is treated as final.
[non-blocking, owner: designer] Figma refresh. The Figma is being updated (date picker representation; removing Programs button / P icon / course-associate button). Low rework risk for the tree/search/outline structure; re-verify the out-of-scope removal list against the refreshed Figma before build.
Context
Decision:[UXD] Build the UI for Competency Criteria Associations #648 is being reframed as a parent UX-design ticket with implementation children. Because this is the first ticket that needs a competency selected, the minimal tree + selection belongs here rather than deferred to a later ticket.
Decision (MVP): the course filter is on course-run start date only; end-date filtering is deferred. React Date Picker (lightly styled with Paragon) fills the gap since Paragon has no native range picker.
Decision: criteria are associated only at the gradeable-subsection level; courses and sections are navigation, not association targets.
Competency tree data: the taxonomy tags list API already returns a competency taxonomy's tags (already satisfied, no backend work); the MFE data layer already carries them (TagData via useTagListData in src/taxonomy/data/apiHooks.ts, nested by TagTree in src/taxonomy/tag-list/tagTree.ts). No backend or data-layer change for the tree.
CourseIndexView (GET /api/contentstore/v1/course_index/{course_id}, openedx-platform): existing full course-outline endpoint (sections→subsections, with graded flag); the likely per-course outline source.
Tree-rendering precedents (frontend; none a drop-in): src/taxonomy/tag-list/tagTree.ts (TagTree nests a taxonomy's tags — the competency tree's basis); src/taxonomy/tree-table/NestedRows/getSubRows (arbitrary-depth recursion, bundled with tag-CRUD context to strip); src/course-outline/OutlineTree.tsx (real 3-level course tree, hardcoded depth and dnd/XBlock-coupled). Reuse TagTree for the competency tree; borrow the recursion pattern for the course outline.
Minimal selectable competency tree for the current taxonomy; reuses TagTree/tree-table recursion; emits the selected competency. No ID badge (separate ticket), no CRUD affordances
Mount CompetencyAssociationsPanel as the page's real content, replacing the placeholder
Implementation Notes
Competency tree (minimal). Reuse useTagListData (src/taxonomy/data/apiHooks.ts), which already returns a taxonomy's tags, and TagTree (src/taxonomy/tag-list/tagTree.ts) to render the current taxonomy's competencies as a nested, selectable tree. The taxonomy id comes from the /taxonomy/:taxonomyId/competencies route (#623). Track the selected competency in CompetencyAssociationsPanel state and pass it (tag id / external_id) to the browse panel as its active competency. Keep it minimal: no ID badge (separate ticket), no create/remove or other CRUD affordances, no switcher/empty-state/responsive polish (#648). Until a selection is made, the right panel shows a "select a competency" prompt and runs no course query.
Course browse. Compose two data sources client-side: #669 for the course list (start-date filter, pagination) and the outline per course (lazily on expand, via CourseIndexView unless #669 returns it inline).
Date filter: compose two DatepickerControl instances into a from/to range bound to #669's start-date parameter; label it as filtering on course start date. Don't send an end-date filter (MVP).
Build CourseOutlineTree as a plain recursive component (borrow NestedRows' childRowsData/depth shape; skip its TanStack table and CRUD context). Filter outline nodes to sections (navigation) and gradeable subsections (the actionable leaves) using the outline's graded flag; don't render unit/component levels. Subsections are read-only here; a gradeable-subsection click exposes a selected-subsection callback ({usageKey, blockType}, mirroring ComponentSelectedEvent) for the #672 select/associate flow to consume — this ticket fires the callback and does nothing else with it.
In frontend-app-authoring, build the Competency Criteria associations workspace at src/taxonomy/competency-management/. CompetencyAssociationsPanel.tsx: a two-panel layout that owns the selected-competency state. Left panel CompetencyTree.tsx: render the current taxonomy's competencies (taxonomy id from the /taxonomy/:taxonomyId/competencies route, #623) as a minimal selectable nested tree, reusing useTagListData (src/taxonomy/data/apiHooks.ts) and TagTree (src/taxonomy/tag-list/tagTree.ts); selecting a competency sets it active and drives the right panel; no ID badge (a separate ticket), no CRUD, no shell polish (#648). Right panel course-search/CourseSearchBrowse.tsx: until a competency is selected show a prompt and run no query; once selected, a title search + a from/to date-range filter built from two src/generic/datepicker-control/DatepickerControl.jsx instances, bound to #669's course-search endpoint (accessible courses filtered by course-run start date only; label it as such; courses with no start date still appear). Paginated course list; each row expands. On expand, render CourseOutlineTree.tsx: fetch the course outline (via the existing CourseIndexView, GET /api/contentstore/v1/course_index/{course_id}, unless the team has #669 return it inline — confirm), a plain recursive renderer (model on src/taxonomy/tree-table/NestedRows.tsx's recursion, without its TanStack table or TreeTableContext) showing sections (navigation) and gradeable subsections (read-only leaves, filtered by the outline graded flag). A gradeable-subsection click fires an onSubsectionSelected({ usageKey, blockType }) callback (mirror ComponentPickerContext's ComponentSelectedEvent) and does nothing else — the #672 flow consumes it. Mount the panel as the #623 page's real content. Do NOT build any backend endpoint, any course-level or section-level association, a Programs button, or a course-associate button. Tests: tree renders and selection drives the panel, no-selection prompt, title search, start-date filtering incl. null-start inclusion, expand-to-outline, empty and error states, small-screen.
Blocked by: #669 (the accessible-courses-by-date endpoint + course outlines) — a TBD contract; wire against it and flag real paths as fill-in-later. The competency tree itself has no new backend dependency — it reuses the existing taxonomy tags list API (a competency is a
Tagin a competency taxonomy; that endpoint already returns them).Repo:
frontend-app-authoring. MFE-only. This ticket is #670; the select/deselect-association UI is #672.Use Case
As a Platform Administrator configuring Competency Criteria associations, I want to select a competency from the taxonomy's competency tree and then search the courses I have access to (filtered by course-run start date), browse a course's sections and gradeable subsections, so that I can find the exact gradeable subsection to attach criteria to without opening each course individually.
Description
Current state
No implementation exists. #623 built only a placeholder Competency Management page (
/taxonomy/:taxonomyId/competencies) whose real content is deferred to future tickets; this is one. There is no left-hand competency tree in code yet: #648 designs the shell and that tree, but no implementation ticket builds it, and this is the first ticket that needs a competency actually selected. Studio's existingStudioHomecourse listing is general and competency-unaware, with no date-range filter and no drill-down into a course's gradeable structure.Requested change
Build the Manage & Apply Competencies associations workspace: a minimal two-panel layout with a competency-selection tree on the left and the course-browsing panel on the right, driven by the selected competency.
CourseIndexView). Sections are navigation; only gradeable subsections are the actionable leaves. For this ticket the subsections are read-only (browse only); making them actionable for association is [Placeholder for FE] Build the select / deselect gradeable-subsection association interactions #672.Figma Design
Explicitly out of scope
Acceptance Criteria
Frontend ticket.
Open Questions
CourseIndexView(GET /api/contentstore/v1/course_index/{course_id}) per expanded course. This ticket's data layer should be written to whichever [BE] Add start-date range filter and outline delivery for accessible courses (Competency Criteria Associations) #669 lands on. [recommended: lazy per-course CourseIndexView call on expand — lighter, reuses an existing endpoint]Context
TagDataviauseTagListDatainsrc/taxonomy/data/apiHooks.ts, nested byTagTreeinsrc/taxonomy/tag-list/tagTree.ts). No backend or data-layer change for the tree.CourseIndexView(GET /api/contentstore/v1/course_index/{course_id},openedx-platform): existing full course-outline endpoint (sections→subsections, withgradedflag); the likely per-course outline source.src/taxonomy/tag-list/tagTree.ts(TagTreenests a taxonomy's tags — the competency tree's basis);src/taxonomy/tree-table/NestedRows/getSubRows(arbitrary-depth recursion, bundled with tag-CRUD context to strip);src/course-outline/OutlineTree.tsx(real 3-level course tree, hardcoded depth and dnd/XBlock-coupled). ReuseTagTreefor the competency tree; borrow the recursion pattern for the course outline.ComponentPickerContext'sComponentSelectedEvent = (c: {usageKey, blockType}) => void, already used for course-structure items viasrc/course-outline/outline-sidebar/AddSidebar.tsx— the shape to mirror for the ([Placeholder for FE] Build the select / deselect gradeable-subsection association interactions #672-bound) subsection-selected callback.src/generic/datepicker-control/DatepickerControl.jsx(wrapsreact-datepicker): the single-date picker to compose into a from/to range.Technical Notes
Files to Create
src/taxonomy/competency-management/associations/CompetencyAssociationsPanel.tsxsrc/taxonomy/competency-management/associations/CompetencyTree.tsxTagTree/tree-table recursion; emits the selected competency. No ID badge (separate ticket), no CRUD affordancessrc/taxonomy/competency-management/course-search/index.tssrc/taxonomy/competency-management/course-search/CourseSearchBrowse.tsxsrc/taxonomy/competency-management/course-search/CourseOutlineTree.tsxNestedRows' recursion pattern, no TanStack/CRUD contextsrc/taxonomy/competency-management/*/messages.tssrc/taxonomy/competency-management/course-search/data/api.tsgetCourseSearchResults(params)(#669),getCourseOutline(courseKey)(CourseIndexView or #669 inline, per decision)src/taxonomy/competency-management/course-search/data/apiHooks.tssrc/taxonomy/competency-management/course-search/data/queryKeys.tssrc/taxonomy/competency-management/**/*.test.tsxFiles to Modify
src/taxonomy/competency-management/index.tsCompetencyAssociationsPanelalongsideCompetencyManagementPage(from #623)CompetencyAssociationsPanelas the page's real content, replacing the placeholderImplementation Notes
Competency tree (minimal). Reuse
useTagListData(src/taxonomy/data/apiHooks.ts), which already returns a taxonomy's tags, andTagTree(src/taxonomy/tag-list/tagTree.ts) to render the current taxonomy's competencies as a nested, selectable tree. The taxonomy id comes from the/taxonomy/:taxonomyId/competenciesroute (#623). Track the selected competency inCompetencyAssociationsPanelstate and pass it (tag id /external_id) to the browse panel as its active competency. Keep it minimal: no ID badge (separate ticket), no create/remove or other CRUD affordances, no switcher/empty-state/responsive polish (#648). Until a selection is made, the right panel shows a "select a competency" prompt and runs no course query.Course browse. Compose two data sources client-side: #669 for the course list (start-date filter, pagination) and the outline per course (lazily on expand, via
CourseIndexViewunless #669 returns it inline).Date filter: compose two
DatepickerControlinstances into a from/to range bound to #669's start-date parameter; label it as filtering on course start date. Don't send an end-date filter (MVP).Build
CourseOutlineTreeas a plain recursive component (borrowNestedRows'childRowsData/depthshape; skip its TanStack table and CRUD context). Filter outline nodes to sections (navigation) and gradeable subsections (the actionable leaves) using the outline'sgradedflag; don't render unit/component levels. Subsections are read-only here; a gradeable-subsection click exposes a selected-subsection callback ({usageKey, blockType}, mirroringComponentSelectedEvent) for the #672 select/associate flow to consume — this ticket fires the callback and does nothing else with it.MFE-only; no ADR. Reuses
frontend-app-authoringconventions (React Query, feature-folder, Paragon,defineMessages).Example Resolution Prompt