Conversation
|
👋 Hi @nairaj2, thanks for contributing! For the review process to begin, please verify that the following is satisfied:
Also check that issue requirements are satisfied & you ran Pull requests that don't follow the guidelines will be closed. Reviewer assignment can take up to 2 weeks. |
|
📢✨ Before we assign a reviewer, we'll turn on |
🟡 Waiting for changesLast updated: 2026-09-15 18:56 UTC |
rtibblesbot
left a comment
There was a problem hiding this comment.
PR #6132: both channel-related ACs are inert — viewsets/channel.py never serializes organization, so the filter holds only "All organizations" and the details page always shows its unavailable message. Specs pass only because fixtures hand-add the field.
CI red: Browser smoke test → Download frontend bundle, a 403 from actions/download-artifact, unrelated here.
Manual QA did not run; nothing visually verified. Unchecked: OrganizationCard sizing inside KCardGrid, .button-container wrapping under 600px.
Design (PDF p3): channels-table Actions column, title info/edit affordances, Publish control — absent. If deferred, say so. P2 annotates cards "0 resources · English", not public/private.
@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly
How was this generated?
Ran a phased review pipeline over the pull request diff:
- Classified the diff to select review passes (core, frontend, backend) and whether manual QA was required
- Core review pass checked correctness, design, architecture, testing, completeness, and DRY/SRP/Rule-of-Three principles
- Specialized frontend/backend review passes applied framework-specific lenses where those files changed
- For UI changes: manual QA and an accessibility audit against a live dev server, when available
- Checked CI status and linked issue acceptance criteria
- Synthesized one review from those passes and chose the verdict from the findings, CI status, and QA evidence
| const filterMap = computed(() => { | ||
| const organizations = new Map(); | ||
| for (const channel of channels.value) { | ||
| if (channel.organization && channel.organization_name) { |
There was a problem hiding this comment.
blocking: organization is in no channel payload — viewsets/channel.py has zero occurrences, despite Channel.organization (models.py:1163). KSelect renders one option forever. Add "organization_id"/"organization__name" to ChannelViewSet.values plus field_map, as OrganizationMemberViewSet does; otherwise make this PR depend on that work.
| const hasOrganizationMetadata = accessibleChannels.some(channel => | ||
| Object.prototype.hasOwnProperty.call(channel, 'organization'), | ||
| ); | ||
| channelsUnavailable.value = !hasOrganizationMetadata; |
There was a problem hiding this comment.
blocking: Same cause: hasOwnProperty(channel, 'organization') is always false, so the Channels tab always renders channelsUnavailable. Sniffing the response shape also hides a missing contract instead of failing tests — drop it once organization ships. It also mislabels "no accessible channels" as "not available yet".
|
|
||
| async function fetchAccessibleChannels() { | ||
| const channelLists = await Promise.all([ | ||
| Channel.where({ edit: true }, true), |
There was a problem hiding this comment.
blocking: ChannelListPagination.page_size = None (viewsets/channel.py:89), so { public: true } pulls every public channel into IndexedDB per page visit; searchCatalog (resources.js:1170) forces page_size for this reason. Add organization to ChannelFilter and fetch Channel.where({ organization }) — one query, no merge, no deleted refilter (line 174), and it can go through shared/vuex/channel/actions.js:loadChannelList like every other channel list.
| </header> | ||
|
|
||
| <nav> | ||
| <KTabsList |
There was a problem hiding this comment.
blocking: Tabs emit aria-controls="organization-details-tabs-<tabId>-panel"; the <section>s (62, 79) have no id, role="tabpanel", aria-labelledby or tabindex, so the reference dangles. Use <KTabsPanel tabsId="organization-details-tabs" :tabId="tabIds.CHANNELS" :activeTabId="activeTab">.
| </p> | ||
| <KCardGrid | ||
| v-else | ||
| layout="1-1-1" |
There was a problem hiding this comment.
blocking: LAYOUT_CONFIG_1_1_1 is one card per row at every breakpoint, so these 360×480 portrait cards form one narrow column; the design shows three across. Use layout="1-2-3". StudioChannelsPage differs — landscape full-width cards.
| renderComponent({}, legacyChannels); | ||
| expect(await screen.findAllByTestId('channel-card')).toHaveLength(2); | ||
| expect( | ||
| await screen.findByText('All organizations', { selector: '.ui-select-display-value' }), |
There was a problem hiding this comment.
suggestion: .ui-select-display-value / .ui-select-option-basic are keen-ui internals vendored by KDS — no other spec queries them, and this PR adds 7. Scope with within(screen.getByTestId('organization-filter')) and query by text.
| expect(await screen.findByRole('heading', { name: 'Learning Together' })).toBeInTheDocument(); | ||
| expect(screen.getByText('Organization channel')).toBeInTheDocument(); | ||
| expect(screen.queryByText('Unrelated channel')).not.toBeInTheDocument(); | ||
| expect(Channel.where).toHaveBeenCalledWith({ edit: true }, true); |
There was a problem hiding this comment.
suggestion: These pin the fetch strategy; 136-137 already assert the behaviour. The server-side filter above would break them with output unchanged. Same at :89.
| analyticsLabel: ListTypeToAnalyticsLabel[listType], | ||
| }); | ||
|
|
||
| if (listType === ChannelListTypes.EDITABLE) { |
There was a problem hiding this comment.
nitpick: lists is fixed, so this is "insert at index 1" by proxy — splice after the loop. analyticsLabel: 'ORGANIZATIONS' bypasses ListTypeToAnalyticsLabel, and the tab ("My organizations") and heading ("Organizations") disagree.
| }, | ||
| }, | ||
| $trs: { | ||
| publicOrganization: 'Public organization', |
There was a problem hiding this comment.
nitpick: Duplicated in OrganizationDetails.$trs — two translator entries per concept. See shared/strings/communityChannelsStrings.js.
| }, | ||
| }); | ||
|
|
||
| // Derive options from the unfiltered list so selecting one organization does not |
There was a problem hiding this comment.
praise: Deriving options from channels, not filteredChannels, avoids the self-narrowing dropdown, and the comment records why. unavailableOrganization fails visibly on an unknown id.
Summary
Adds frontend support for organizations in Studio:
This is a frontend-only change and builds on the organization models and endpoints introduced previously.
References
Reviewer guidance
To verify manually:
Automated verification performed:
All 24 channel-list test suites and 215 tests pass. Pre-commit frontend checks also pass.
The organization channel list depends on channel responses containing organization association metadata. If that metadata is unavailable, the details page displays an unavailable-state message rather than showing unrelated channels.
Because this PR changes several layouts and workflows, screenshots of My channels, My organizations, and the organization details page would help review.
AI usage
I used OpenAI Codex to analyze the issue, design document, and reference PR; implement the organization pages and channel filter; and write frontend tests. I reviewed and refined the generated changes against the project conventions, corrected invalid API query behavior found during local testing, limited the implementation to frontend files, and ran the focused and full channel-list test suites plus pre-commit checks.