Build an Invite Flow widget providing an email invite form with role selection, a pending invite list with resend/revoke actions, and visual state management for accepted, expired, and pending invites. No invite-related widget currently exists in the design system.
Introduction
Overview
Team invitation flows are one of the most common SaaS patterns. Every multi-tenant application needs to invite users by email, assign a role, track invite status, and allow resending or revoking pending invites. The design system has no widget for this. Teams build it from scratch using TextField, Select, DataGrid, and Badge primitives, producing inconsistent implementations.
Goals
Provide an InviteFlow widget with an email invite form, role selector, and send action.
Display a list of pending, accepted, and expired invites with appropriate status badges.
Support resend and revoke actions on pending invites.
Handle multi-email input (comma-separated or one-at-a-time).
Validate email format client-side before sending.
Ship with Storybook stories and unit tests.
Non-Goals
Actual email sending (backend concern; widget calls callbacks).
Invite link generation or deep-link management.
Permission/RBAC management beyond role selection at invite time.
Scope
In Scope
Item
Description
Invite form
Email input (single or multi), role selector, “Send Invite” button.
Pending invite list
Table/list showing invitee email, role, status, sent date, and actions.
Button to resend a pending invite; calls onResend callback.
Revoke action
Button with confirmation to revoke a pending invite; calls onRevoke callback.
Email validation
Client-side email format validation before sending.
Multi-email input
Support comma-separated emails or a tag-input pattern.
State handling
Loading, empty (“No invites sent yet”), and error states.
Stories and tests
Full Storybook coverage; unit tests for form validation and actions.
Out of Scope
Item
Reason
Email delivery
Backend concern.
Invite link/token generation
Backend concern.
Post-accept onboarding
Separate flow.
Bulk CSV import
Future enhancement; initial version supports manual entry.
Users and Pain Points
User
Pain Point
SaaS developers
Build invite flows from scratch for every multi-tenant application.
Team admins
No consistent UX for inviting members, checking invite status, or resending expired invites.
Product managers
Invite flow inconsistencies lead to confusion about invite status across different products.
New users
Unclear whether their invite is pending, expired, or already accepted.
Definitions
Term
Definition
Invite
An email invitation to join a team/organization with a specific role.
Pending
Invite sent but not yet accepted by the recipient.
Accepted
Invite accepted; the user has joined the team.
Expired
Invite not accepted within the validity period.
Revoked
Invite manually canceled by an admin before acceptance.
Role
The permission level assigned to the invitee (e.g., “Admin”, “Member”, “Viewer”).
Current State
No invite-related widget exists in the design system.
TextField: Available for email input.
Select: Available for role selection.
DataGrid: Available for invite list display.
Badge: Available for status badges.
Button: Available for actions.
AlertDialog: Available for revoke confirmation.
Proposed Solution
Build src/components/widgets/invite-flow.tsx with two main sections:
Invite Form
A horizontal form row (or stacked on mobile) containing:
Email input — A TextField with email validation. Supports multi-email via a tag-input variant: typed emails become removable tags. Validates each email on add.
Role selector — A Select dropdown populated by consumer-provided roles.
Send button — Calls onSendInvites({ emails, role }) callback. Disabled while sending (loading state on button).
Invite List
A table or styled list below the form showing all invites:
Column
Content
Email
Invitee email address
Role
Role badge
Status
Pending/Accepted/Expired/Revoked badge
Sent
Relative timestamp
Actions
Resend (pending/expired), Revoke (pending)
Resend is available for pending and expired invites. Revoke opens an AlertDialog confirmation. Accepted and revoked invites show no actions (or a “Remove” action for accepted members, deferred to the Role Assignment Panel).
State Handling
Loading: Skeleton form and list.
Empty: “No invites sent yet. Invite your first team member above.”
Error: StatusCard with retry.
Requirements
Requirement Priorities
Must Have: Invite form with email + role, invite list with status badges, send/resend callbacks.
Should Have: Multi-email tag input, revoke with confirmation, expired state handling.
Use a permissive regex; server-side validation is authoritative.
Invite list grows long for large teams
Low
Medium
Add pagination at 50+ invites; DataGrid supports this natively.
Open Questions
#
Question
Owner
Status
OQ-01
Should the form support a custom message field that accompanies the invite email?
David Holmes
Open
OQ-02
Should accepted invites appear in this list or only in the team member list (RoleAssignmentPanel)?
David Holmes
Open
OQ-03
Should the widget support a “Copy invite link” alternative for non-email invites?
David Holmes
Open
Acceptance Criteria
#
Criterion
AC-01
Invite form validates email format and sends invites with selected role via onSendInvites.
AC-02
Multi-email input allows adding multiple emails as removable tags.
AC-03
Invite list displays all invites with correct status badges.
AC-04
Resend action calls onResend for pending and expired invites.
AC-05
Revoke action shows confirmation dialog and calls onRevoke.
AC-06
Loading, empty, and error states render appropriate visual treatments.
AC-07
All components pass axe-core checks with zero violations.
AC-08
Storybook stories exist for all states and interaction flows.
AC-09
pnpm typecheck and pnpm vitest run --project unit pass with zero errors.
LLM Handoff Instructions
When implementing this FRD:
Createsrc/components/widgets/invite-flow.tsx.
Invite form: Horizontal flex row with TextField (email), Select (role), Button (send). For multi-email, build a simple tag input: on comma or Enter, validate the email and add it as a tag. Tags are removable chips.
Invite list: Use a styled list (or DataGrid for larger lists) showing email, role badge, status badge, sent date, and action buttons.
Status badges: Use Badge with variant mapped from invite status — pending→warning, accepted→complete, expired→default (muted), revoked→destructive.
Actions: Resend is a ghost Button. Revoke uses AlertDialog with destructive variant.
State handling: Loading shows Skeleton; empty shows StatusCard with CTA pointing to the form; error shows StatusCard with retry.