Skip to content

feat(committees): allow direct member add without invite (LFXV2-2227)#953

Open
andrest50 wants to merge 1 commit into
mainfrom
feat/LFXV2-2227
Open

feat(committees): allow direct member add without invite (LFXV2-2227)#953
andrest50 wants to merge 1 commit into
mainfrom
feat/LFXV2-2227

Conversation

@andrest50

Copy link
Copy Markdown
Contributor

Summary

  • Adds an Add directly vs Send invite toggle to the committee Add Member dialog so writers can add people to the roster without triggering the invite flow
  • Direct add calls createCommitteeMember; invite mode preserves the existing createCommitteeInvite bulk behavior
  • Search-selected profiles enrich direct-add payloads (name, username, job title) when available

Test plan

  • Open a committee Members page as a writer and click Add Member
  • With Add directly selected, add a known LF user via search — confirm they appear as an active member (not pending invite)
  • With Add directly selected, paste an email manually — confirm member is created without a pending invitation
  • Switch to Send invite and add an email — confirm pending invite appears and no direct member is created
  • Verify bulk add (multiple emails) works in both modes with appropriate success/partial-failure toasts
  • Confirm existing members and already-invited emails are still skipped in both modes

Made with Cursor

Writers can choose between adding members directly to the roster or
sending a pending invite from the Add Member dialog.

Signed-off-by: Andres Tobon <andrest2455@gmail.com>
@andrest50 andrest50 requested a review from a team as a code owner June 15, 2026 20:13
Copilot AI review requested due to automatic review settings June 15, 2026 20:13
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The add-member dialog gains a two-mode submission flow: "Send invite" (existing behavior) and "Add directly to roster" (new). Shared contracts (AddMemberActionMode type, ADD_MEMBER_ACTION_OPTIONS constant) are added to the shared package. The component adds an actionMode form control, an emailProfiles signal, computed submitLabel/submitIcon signals, and branches onSubmit into submitInvites and submitDirectAdds with separate result summarization and error mapping. The template binds all UI elements to the active mode.

Changes

Add-member dialog dual-mode support

Layer / File(s) Summary
Shared type and options constant
packages/shared/src/interfaces/committee.interface.ts, packages/shared/src/constants/committees.constants.ts
AddMemberActionMode union type (invite | add_directly) and ADD_MEMBER_ACTION_OPTIONS constant array are exported for use by the component and template.
Component state, signals, and wiring
add-member-dialog.component.ts
Adds emailProfiles signal keyed by normalized email, actionMode form control with default, actionModeOptions, and computed submitLabel/submitIcon/invalidSummary signals. Adds SelectButtonComponent to imports. Stores selected profiles in addEmail(...).
Submit branching and direct-add logic
add-member-dialog.component.ts
onSubmit branches by actionMode to submitInvites or submitDirectAdds. Adds buildMemberRequest (enriches payload from emailProfiles), submitDirectAdds (bounded fan-out), summarizeAddResults (toast messaging), and addFailureReason (409 → "already a member").
Template mode-aware UI bindings
add-member-dialog.component.html
Renders the actionMode selector with conditional help text, updates the "no matches" hint, conditionally changes the preview icon/label for invite vs add count, and binds the submit button to submitLabel()/submitIcon().

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

enhancement

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main feature: enabling direct member addition without invite, which is the primary change across all modified files.
Description check ✅ Passed The description is directly related to the changeset, clearly explaining the new toggle feature, the two action modes, and providing a comprehensive test plan aligned with the implementation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/LFXV2-2227

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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

Copilot AI 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.

Pull request overview

This PR updates the Committees “Add Member” dialog to support two writer-controlled submission modes: adding members directly to the roster (via createCommitteeMember) or sending pending invites (existing createCommitteeInvite fan-out), with optional enrichment of direct-add payloads using search-selected profile data.

Changes:

  • Introduces an AddMemberActionMode type and corresponding UI option constants for the dialog mode toggle.
  • Updates the Add Member dialog UI/logic to switch between invite flow and direct-add flow on submit.
  • Adds payload enrichment for direct-adds using cached search result profiles (username/name/job title) when available.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
packages/shared/src/interfaces/committee.interface.ts Adds AddMemberActionMode union type for dialog submission behavior.
packages/shared/src/constants/committees.constants.ts Adds ADD_MEMBER_ACTION_OPTIONS used by the new mode toggle UI.
apps/lfx-one/src/app/modules/committees/components/add-member-dialog/add-member-dialog.component.ts Implements mode-driven submit behavior (invite vs direct add) and direct-add request building/toast summaries.
apps/lfx-one/src/app/modules/committees/components/add-member-dialog/add-member-dialog.component.html Adds the mode toggle UI and updates copy/preview/button label+icon based on selected mode.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


@if (!searchLoading() && searchResults().length === 0 && queryValue().length >= 2) {
<p class="text-xs text-gray-400">No matches for "{{ queryValue() }}". You can still invite them by typing their email below.</p>
<p class="text-xs text-gray-400">No matches for "{{ queryValue() }}". You can still add them by typing their email below.</p>
Comment on lines 81 to 85
public readonly form = new FormGroup({
actionMode: new FormControl<AddMemberActionMode>('add_directly', { nonNullable: true }),
emails: new FormControl<string>('', { nonNullable: true }),
role: new FormControl<string | null>(null),
});
Comment on lines +224 to +234
private buildMemberRequest(email: string, role: string | null): CreateCommitteeMemberRequest {
const profile = this.emailProfiles().get(email);
return {
email,
username: profile?.username ?? null,
first_name: profile?.first_name ?? null,
last_name: profile?.last_name ?? null,
job_title: profile?.job_title ?? null,
role: role ? { name: role as CommitteeMemberRole, start_date: null, end_date: null } : null,
};
}
Comment on lines +306 to +312
private addFailureReason(err: HttpErrorResponse): string {
if (err.status === 409) {
return 'already a member';
}
const upstream = typeof err.error?.message === 'string' ? err.error.message : null;
return upstream ?? 'add failed';
}

@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.

🧹 Nitpick comments (1)
apps/lfx-one/src/app/modules/committees/components/add-member-dialog/add-member-dialog.component.ts (1)

14-27: ⚡ Quick win

Use direct shared-module imports instead of shared barrel imports in this standalone component.

Line 14 and Line 17 currently import from barrel paths (@lfx-one/shared/constants, @lfx-one/shared/interfaces). Please switch these to direct shared module paths to match the standalone import rule.

♻️ Suggested import update
-import { ADD_MEMBER_ACTION_OPTIONS, COMMITTEE_INVITE_CONCURRENCY, MEMBER_ROLES } from '`@lfx-one/shared/constants`';
-import { CommitteeMemberRole } from '`@lfx-one/shared/enums`';
+import { ADD_MEMBER_ACTION_OPTIONS, COMMITTEE_INVITE_CONCURRENCY, MEMBER_ROLES } from '`@lfx-one/shared/constants/committees.constants`';
+import { CommitteeMemberRole } from '`@lfx-one/shared/enums/committee-member.enum`';
 import {
   AddMemberActionMode,
   CategorizedCommitteeEmails,
   Committee,
   CommitteeInvite,
   CommitteeInviteResult,
   CommitteeMember,
   CreateCommitteeMemberRequest,
   DecoratedCommitteeSearchResult,
   EmailListParseResult,
   UserSearchResult,
-} from '`@lfx-one/shared/interfaces`';
+} from '`@lfx-one/shared/interfaces/committee.interface`';

As per coding guidelines: **/*.{ts,tsx} should use direct imports for standalone components (no barrel exports) and use @lfx-one/shared/* direct paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/lfx-one/src/app/modules/committees/components/add-member-dialog/add-member-dialog.component.ts`
around lines 14 - 27, The add-member-dialog.component.ts file is a standalone
component that is currently using barrel imports from `@lfx-one/shared/constants`
and `@lfx-one/shared/interfaces`, which violates the standalone component import
rule. Replace the barrel imports for ADD_MEMBER_ACTION_OPTIONS,
COMMITTEE_INVITE_CONCURRENCY, MEMBER_ROLES (from `@lfx-one/shared/constants`) and
all the interface imports like AddMemberActionMode, CategorizedCommitteeEmails,
Committee, CommitteeInvite, CommitteeInviteResult, CommitteeMember,
CreateCommitteeMemberRequest, DecoratedCommitteeSearchResult,
EmailListParseResult, UserSearchResult (from `@lfx-one/shared/interfaces`) with
direct shared module paths following the `@lfx-one/shared/`* convention as
required for standalone components.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@apps/lfx-one/src/app/modules/committees/components/add-member-dialog/add-member-dialog.component.ts`:
- Around line 14-27: The add-member-dialog.component.ts file is a standalone
component that is currently using barrel imports from `@lfx-one/shared/constants`
and `@lfx-one/shared/interfaces`, which violates the standalone component import
rule. Replace the barrel imports for ADD_MEMBER_ACTION_OPTIONS,
COMMITTEE_INVITE_CONCURRENCY, MEMBER_ROLES (from `@lfx-one/shared/constants`) and
all the interface imports like AddMemberActionMode, CategorizedCommitteeEmails,
Committee, CommitteeInvite, CommitteeInviteResult, CommitteeMember,
CreateCommitteeMemberRequest, DecoratedCommitteeSearchResult,
EmailListParseResult, UserSearchResult (from `@lfx-one/shared/interfaces`) with
direct shared module paths following the `@lfx-one/shared/`* convention as
required for standalone components.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3afcd716-4aec-4200-90cc-d4005ae61c9d

📥 Commits

Reviewing files that changed from the base of the PR and between b1335e9 and 4de21b0.

📒 Files selected for processing (4)
  • apps/lfx-one/src/app/modules/committees/components/add-member-dialog/add-member-dialog.component.html
  • apps/lfx-one/src/app/modules/committees/components/add-member-dialog/add-member-dialog.component.ts
  • packages/shared/src/constants/committees.constants.ts
  • packages/shared/src/interfaces/committee.interface.ts

@github-actions

Copy link
Copy Markdown

🚀 Deployment Status

Your branch has been deployed to: https://ui-pr-953.dev.v2.cluster.linuxfound.info

Deployment Details:

  • Environment: Development
  • Namespace: ui-pr-953
  • ArgoCD App: ui-pr-953

The deployment will be automatically removed when this PR is closed.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants