Skip to content

feat(admin): list pending invites in user details side panel - #1847

Open
Shreyag02 wants to merge 4 commits into
mainfrom
feat/user-details-pending-invites
Open

feat(admin): list pending invites in user details side panel#1847
Shreyag02 wants to merge 4 commits into
mainfrom
feat/user-details-pending-invites

Conversation

@Shreyag02

Copy link
Copy Markdown
Contributor

Summary

The user details side panel now lists the organizations a user has been invited to but hasn't joined yet, sitting below their existing memberships. Data comes from FrontierService/ListUserInvitations.

Changes

  • New Invitations section in the user details side panel — one block per invite, showing the org, the role offered, invite status (Pending / Expired), and a relative expiry ("5 days left", flipping to "5 days ago" once lapsed).
  • New useOrganizationRoles hook (admin/hooks/) that fetches the platform's default org roles plus the org's custom roles, returning both a roles list and an id → title map.
  • membership-dropdown.tsx now uses that hook (−40 lines). It previously had its own copy of the same two queries, so both places resolve role titles identically from here on.

Technical Details

  • ListUserInvitations is keyed by the invitee's email, not the user uuid — an invitation can exist before the user does. Worth knowing because passing the uuid returns 200 with an empty list rather than an error, so getting it wrong fails silently. The query uses user?.email and is gated on it.
  • The API returns expired invitations too. There's no server-side expires_at filter; rows live until the cleanup cron sweeps them. So Pending / Expired is derived client-side. formatExpiry diffs forwards on either side of "now" and swaps only the suffix, so live and lapsed invites always read consistently.
  • An invitation carries only org_id and role_ids, so the org title/avatar comes from the existing useOrganizationLookup and role titles from the new hook. react-query caches both per key, so multiple invite blocks share one fetch.
  • Invitations and memberships have independent loading/error branches — a failure in one won't blank out the other section.
  • Role fetch errors now log once from a useEffect inside the hook. The dropdown previously logged them from the render body, which repeated on every render.

Test Plan

  • Manual testing completed
  • Build and type checking passes

Verified so far:

  • tsc --noEmit error set is byte-identical before and after the change (21 pre-existing errors, all in untouched files; 0 introduced) — confirmed by stashing the branch and re-running against main.

  • eslint clean on all four touched files; pnpm build succeeds in web/sdk.

  • Expiry formatting checked against the real payload (expires_at``2026-08-11T10:00:21Z) and across the boundaries:

    Case Renders
    7-day invite, created today 6 days left
    1 day remaining 1 day left (singular)
    3 hours remaining 3 hours left
    under an hour, either side Less than an hour left / ago
    lapsed 5 days ago 5 days ago

SQL Safety (if your PR touches *_repository.go or goqu.*)

Not applicable — frontend only, no Go or query-builder changes.

  panel and extract org role fetching into useOrganizationRoles
@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
frontier Ready Ready Preview Aug 7, 2026 11:57am

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6745029a-f89c-48e3-955e-2f7beb2e2a49

📥 Commits

Reviewing files that changed from the base of the PR and between 9b8c987 and 54da7cf.

📒 Files selected for processing (2)
  • web/sdk/admin/utils/connect-timestamp.ts
  • web/sdk/admin/views/users/details/layout/side-panel-invitation.tsx

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added invitation details to the user details side panel, including organization, role, status, and expiration information.
    • Added loading placeholders and error handling while invitation details are retrieved.
    • Consolidated platform and organization role loading for consistent role names across the admin interface.
    • Added clear expired-invitation messaging and relative expiration times.

Walkthrough

This PR adds shared organization role retrieval and uses it in MembershipDropdown. It adds invitation querying to the user details side panel. It adds SidePanelInvitation for invitation details, role titles, status, and expiry text.

Changes

Organization roles and invitation panel

Layer / File(s) Summary
Shared organization role retrieval
web/sdk/admin/hooks/useOrganizationRoles.ts
Adds a hook that fetches platform and organization roles, merges them, builds a role-title lookup, and exposes loading and error state.
Membership role integration
web/sdk/admin/views/users/details/layout/membership-dropdown.tsx
Replaces direct role queries and manual state aggregation with useOrganizationRoles.
Invitation query wiring
web/sdk/admin/views/users/details/layout/side-panel.tsx
Queries invitations by user email, normalizes the response, and renders invitation sections with loading and error states.
Invitation detail rendering and expiry formatting
web/sdk/admin/utils/connect-timestamp.ts, web/sdk/admin/views/users/details/layout/side-panel-invitation.tsx
Adds invite-specific relative expiry formatting and renders organization identity, roles, status, avatar, and expiry details.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • raystack/frontier#1492: Both PRs add organization invitation handling and invitation status or detail display.
  • raystack/frontier#1664: Both PRs modify admin organization role-management flows and role-related UI components.

Suggested reviewers: rohanchkrabrty

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 49158fa7-268d-4f6e-adf1-52556c80c8a0

📥 Commits

Reviewing files that changed from the base of the PR and between ad5e9a5 and 9b8c987.

📒 Files selected for processing (4)
  • web/sdk/admin/hooks/useOrganizationRoles.ts
  • web/sdk/admin/views/users/details/layout/membership-dropdown.tsx
  • web/sdk/admin/views/users/details/layout/side-panel-invitation.tsx
  • web/sdk/admin/views/users/details/layout/side-panel.tsx

Comment thread web/sdk/admin/views/users/details/layout/side-panel-invitation.tsx Outdated
Comment on lines +33 to +51
const isExpired = !expires.isAfter(now);
const [from, to] = isExpired ? [expires, now] : [now, expires];
const suffix = isExpired ? "ago" : "left";

const days = to.diff(from, "day");
if (days >= 1) {
return { text: `${days} day${days === 1 ? "" : "s"} ${suffix}`, isExpired };
}

const hours = to.diff(from, "hour");
if (hours >= 1) {
return {
text: `${hours} hour${hours === 1 ? "" : "s"} ${suffix}`,
isExpired,
};
}

return { text: `Less than an hour ${suffix}`, isExpired };
}

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.

We can use dayjs relativeTime plugin
https://day.js.org/docs/en/plugin/relative-time

@coveralls

coveralls commented Aug 6, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 31176125718

Coverage remained the same at 47.99%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 39850
Covered Lines: 19124
Line Coverage: 47.99%
Coverage Strength: 15.37 hits per line

💛 - Coveralls

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.

3 participants