feat(rbac): no role at registration; invitation-based membership; nav gating

- Registration no longer asks for a role/persona (removed the role select +
  allottee block); crm.account.register sends no persona.
- New users have no permissions → the sidebar now shows only Dashboard + Profile
  for them, gated on crm.account.me (membership + permissions). Members see the
  areas their permissions allow.
- Add /portal/invite?token=… : accepts the invite for a signed-in registered
  user, or routes an unregistered invitee through register/onboarding, which
  redeems the stashed token on completion (granting the invited role).
- Team Management: 'Copy link' on pending invites builds the invite link from
  the invitation token (no email delivery yet).
This commit is contained in:
tanweer919
2026-07-13 16:25:53 +05:30
parent 68bf137d11
commit 103f3ae3a1
6 changed files with 220 additions and 66 deletions
+4 -2
View File
@@ -38,6 +38,8 @@ export interface UiMember {
}
export interface UiInvite {
id: string; email: string; roleIds: string[]; invitedBy: string; sentAt: string;
/** Accept token for the invite link (pending invites only; no email delivery yet). */
token?: string;
}
export interface TeamData {
@@ -70,7 +72,7 @@ const initialsOf = (name: string) =>
interface RoleRef { id: string; slug: string; name: string; color: string | null; isSystem: boolean; isOwnerRole: boolean }
interface MemberDTO { id: string; principalId: string; jobTitle: string | null; joinedAt: string; status: "active" | "deactivated"; roles: RoleRef[]; openDeals: number }
interface RoleDTO { id: string; slug: string; name: string; description: string | null; color: string | null; isSystem: boolean; isOwnerRole: boolean; permissions: string[]; memberCount: number }
interface InvitationDTO { id: string; email: string; roles: RoleRef[]; invitedBy: string; createdAt: string }
interface InvitationDTO { id: string; email: string; roles: RoleRef[]; invitedBy: string; createdAt: string; token?: string }
const relTime = (iso: string): string => {
const then = Date.parse(iso);
@@ -169,7 +171,7 @@ function useLiveTeam(): TeamData {
const invites: UiInvite[] = useMemo(() => (invitesQ.data?.items ?? []).map((i) => ({
id: i.id, email: i.email, roleIds: i.roles.map((r) => r.id),
invitedBy: i.invitedBy, sentAt: relTime(i.createdAt),
invitedBy: i.invitedBy, sentAt: relTime(i.createdAt), token: i.token,
})), [invitesQ.data]);
return {