Skip to content

Add Organizations' User Invitation UI - #6126

Draft
yasinelmi wants to merge 2 commits into
learningequality:unstablefrom
yasinelmi:issue-6088-invitation-ui
Draft

Add Organizations' User Invitation UI#6126
yasinelmi wants to merge 2 commits into
learningequality:unstablefrom
yasinelmi:issue-6088-invitation-ui

Conversation

@yasinelmi

@yasinelmi yasinelmi commented Sep 2, 2026

Copy link
Copy Markdown

Summary

Implements the frontend for #6088: a My Organizations landing page, an organization
edit page with Details and Sharing tabs (invite/manage members), and organization
creation — following the existing channel edit/details page pattern.

Closes #6088

New pages and features

Screenshots below are placeholders — see "How to reproduce these screenshots" underneath
for exactly what to capture. Log in as a@a.com / a on a local pnpm devsetup instance
(seeded via python manage.py setup, which now also creates two sample organizations and
a pending invitation).

Description Screenshot
My Organizations landing page — reached via the new "My organizations" nav tab. Shows a banner of pending organization invitations (accept/decline) above a card grid of the organizations you belong to, each showing your role in it. (screenshot: /#/my-organizations)
Organization card options menu — the "⋮" button on each card opens a dropdown with "Edit organization," which goes to the same edit page as clicking the card body. (screenshot: click the ⋮ on any org card)
New organization form — reached via "New organization" on the landing page. Same page/component as editing an existing org, just blank, with no tabs (nothing to share yet) and a "Create organization" button instead of "Save changes." The creator automatically becomes the org's admin. (screenshot: click "New organization")
Organization edit page — Details tab (as admin) — name/description/public checkbox, editable. Header uses the same dark "immersive modal" banner as the channel details page, including its focus trap and Escape-to-close behavior. (screenshot: open an org you're admin of → Details tab)
Organization edit page — Details tab (as a non-admin member) — same page, but fields are disabled with an explanatory notice ("Only organization admins can edit these details") instead of silently failing on save. (screenshot: open an org where your role is Editor/Viewer)
Sharing tab — invite + manage members — only visible to org admins. An invite-by-email form with a role selector (Viewer/Editor/Admin), plus a single table unifying active members and pending invitations, each with a "⋮" menu (role change / remove for members; resend / revoke for pending invites). (screenshot: Sharing tab, as admin)
Sharing tab — as a non-admin — replaced entirely with a "Only organization admins can manage sharing settings" message; no invite form or member table rendered at all. (screenshot: Sharing tab, as Editor/Viewer)
Remove-member / revoke-invitation confirmation — both are destructive actions gated behind a confirmation modal naming the affected email. (screenshot: ⋮ → "Remove from organization" or "Revoke invitation" on a row)
Last-active-admin protection — the API refuses to demote, deactivate, or remove an organization's only active admin; the UI surfaces the exact server error as a snackbar instead of failing silently. (screenshot: as the sole admin, try "Make viewer" or "Remove from organization" on yourself)

Framework compliance (per the issue's Frameworks checklist)

  • No Vuetify anywhere in the new code — every component uses Kolibri Design System
    (KCard, KButton, KTextbox, KCheckbox, KSelect, KTable, KTabsList,
    KModal, KDropdownMenu, StudioImmersiveModal).
  • No Vuex — all new state is Composition API composables
    (useOrganization, useOrganizationList, useOrganizationMembers,
    useOrganizationInvitations).

Accessibility

  • Every icon-only button has an ariaLabel/tooltip; the options button on each
    organization card carries a per-card distinguishing label ("More options for
    {org name}") rather than a generic repeated one.
  • Keyboard focus is trapped inside the edit page while open (via StudioImmersiveModal's
    KFocusTrap), with Escape closing it — matching the existing channel details page.
  • The document/tab title updates on every route and tab change, so screen readers get a
    cue that the SPA navigated even though there's no real page reload.
  • Disabled form fields for non-admins are paired with visible explanatory text, not just
    a visual dimming.

Test plan

  • pytest — organization, organization-role-annotation, invitation, and channel
    viewset tests pass (219 backend tests)
  • pnpm test — full channelList frontend suite passes (226 tests)
  • pre-commit run --all-files on all touched files
  • Manually verified in the browser: invitation accept/decline, invite-by-email, role
    changes, resend/revoke invites, member removal (including the last-active-admin
    guard), organization creation, and the non-admin read-only states

Notes for reviewers

  • No Vuex and no Vuetify anywhere in the new code (all new state is Composition API
    composables) per the issue's Frameworks requirements.
  • Draft PR — opening for visibility into the current state of the changes while this is
    still being reviewed/iterated on. Screenshots to be added shortly.

🤖 Generated with Claude Code

Adds the frontend for issue learningequality#6088: a My Organizations landing page
(pending-invitations banner + org card grid), an organization edit page
with Details and Sharing tabs, an invite-by-email form, and a unified
members/pending-invites table with role change, remove, resend, and
revoke actions. Reuses StudioImmersiveModal for the edit page so it
matches the channel edit/details page pattern, including its focus
trap and dark banner header. Also supports creating a new organization
through the same page.

All new state is Composition API composables (no new Vuex), and every
new component uses Kolibri Design System components exclusively (no
Vuetify).

Backend: adds a `revoke` action and organization support to
InvitationViewSet, and adds a `role` annotation on top of learningequality#6080's
OrganizationViewSet so the frontend can show the current user's role
in an organization without an extra request per organization.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@learning-equality-bot

Copy link
Copy Markdown

👋 Hi @yasinelmi, thanks for contributing!

For the review process to begin, please verify that the following is satisfied:

  • Contribution is aligned with our contributing guidelines

  • Pull request description has correctly filled AI usage section & follows our AI guidance:

    AI guidance

    State explicitly whether you didn't use or used AI & how.

    If you used it, ensure that the PR is aligned with Using AI as well as our DEEP framework. DEEP asks you:

    • Disclose — Be open about when you've used AI for support.
    • Engage critically — Question what is generated. Review code for correctness and unnecessary complexity.
    • Edit — Review and refine AI output. Remove unnecessary code and verify it still works after your edits.
    • Process sharing — Explain how you used the AI so others can learn.

    Examples of good disclosures:

    "I used Claude Code to implement the component, prompting it to follow the pattern in ComponentX. I reviewed the generated code, removed unnecessary error handling, and verified the tests pass."

    "I brainstormed the approach with Gemini, then had it write failing tests for the feature. After reviewing the tests, I used Claude Code to generate the implementation. I refactored the output to reduce verbosity and ran the full test suite."

Also check that issue requirements are satisfied & you ran pre-commit locally.

Pull requests that don't follow the guidelines will be closed.

Reviewer assignment can take up to 2 weeks.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Organizations' User Invitation UI

2 participants