assessment-model/src
Khalim Conn-Kowlessar d70b15e705 Unify invitation flow: explicit accept/decline via profile-menu notifications
Replaces the auto-accept-on-signin behaviour with an explicit accept or
decline step. Every invitee (existing user or brand new) is now treated
the same way: an invitation lands in portfolio_invitations, the email
is a deep-link "Sign in to Ara", and the invitee accepts (or declines)
explicitly from a notifications panel hanging off their profile avatar.

Why: the previous flow silently added existing users to portfolios with
no way to refuse, and gave them no in-app confirmation that the invite
landed. Existing users complained they didn't know which account they
were signed in as either — both gaps are closed by the same panel.

Backend:
  - POST /collaborators no longer creates portfolioUsers directly for
    existing users; it writes a portfolio_invitations row in every case
    except the trivial "already a member of THIS portfolio" path
    (silent role update, no email)
  - New /api/user/invitations endpoint: GET lists pending invitations
    addressed to the current user across all portfolios, joined with
    portfolio + inviter context; POST accepts or declines a single
    invitation, scoped to session.email so users can't act on others'
  - Accept reuses the existing planInvitationApplication helper for
    the "already a member" idempotency check
  - Decline is a silent delete (matches GitHub/Linear/Notion convention;
    no email to inviter, no tombstone)
  - signIn callback no longer auto-applies pending invitations — that
    block is removed entirely along with its now-unused imports
  - Email template subject + body unified, no longer suggests the user
    is "added"; both modes say "invited" and direct them to the profile
    menu

Frontend:
  - ProfileDropDown rewritten as a notifications panel: shows the
    signed-in email at the top (closing the "which account?" gap),
    lists pending invitations with Accept/Decline buttons, displays a
    red count badge on the avatar (max "9+"). Uses TanStack Query with
    optimistic update on accept/decline and toast on outcome. Existing
    Help + Sign Out menu items preserved.
  - No useEffect — pending-count derived from query data, mutations
    handle the rest

Vercel preview test plan:
  - Invite a user already in another portfolio → red badge appears on
    their next page load; Accept lands them in the portfolio
  - Invite a new email → sign-up flow finishes; new account lands on
    home with a badge waiting for Accept (no longer auto-accepted)
  - Existing member of THIS portfolio re-invited → silent role update,
    no email

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 09:43:13 +00:00
..
app Unify invitation flow: explicit accept/decline via profile-menu notifications 2026-05-28 09:43:13 +00:00
lib removed finalise code as its needed only in the final new process 2026-05-06 16:05:24 +00:00
types next js 2025-12-08 12:20:07 +00:00
middleware.ts fixed filtering logic 2026-02-07 19:59:51 +00:00