Skip to content

feat(meetings): add public meeting registration and RSVP updates#193

Merged
asithade merged 2 commits intomainfrom
feat/LFXV2-862
Dec 3, 2025
Merged

feat(meetings): add public meeting registration and RSVP updates#193
asithade merged 2 commits intomainfrom
feat/LFXV2-862

Conversation

@asithade
Copy link
Contributor

@asithade asithade commented Dec 3, 2025

Summary

  • Add public meeting self-registration for non-invited users on public, non-restricted meetings
  • Add register button to meeting card and meeting join pages
  • Add public registration modal component with form validation
  • Add server endpoint POST /public/api/meetings/register for public meeting registration
  • Allow users to change their RSVP response after initial selection
  • Add invited status check to public meeting endpoint for authenticated users
  • Add meeting helper for user invitation status checks
  • Fix dashboard section header height alignment (h-8, leading-8)
  • Add M2M token support for public meeting operations

JIRA Tickets

  • LFXV2-862 - Public meeting self-registration for non-invited users
  • LFXV2-863 - Allow users to change their RSVP response
  • LFXV2-864 - Fix dashboard section header height alignment

🤖 Generated with Claude Code

LFXV2-862 LFXV2-863 LFXV2-864

- Add public meeting self-registration for non-invited users
- Add register button to meeting card and meeting join pages
- Add public registration modal component
- Add server endpoint for public meeting registration
- Allow users to change their RSVP response after initial selection
- Add invited status check to public meeting endpoint
- Add meeting helper for user invitation status
- Fix dashboard section header height alignment (h-8, leading-8)
- Add M2M token support for public meeting operations

Signed-off-by: Asitha de Silva <asithade@gmail.com>
Copilot AI review requested due to automatic review settings December 3, 2025 21:52
@asithade asithade requested a review from jordane as a code owner December 3, 2025 21:52
@coderabbitai
Copy link

coderabbitai bot commented Dec 3, 2025

Caution

Review failed

The pull request is closed.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds public meeting registration for unauthenticated users, a registration modal, server endpoint and helpers to compute invited status, UI conditionals for register vs RSVP, minor RSVP and styling adjustments, and shared Meeting interface update to include invited: boolean.

Changes

Cohort / File(s) Change Summary
Styling & Layout
apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.html, apps/lfx-one/src/app/modules/dashboards/components/pending-actions/pending-actions.component.html, apps/lfx-one/src/styles.scss
Minor presentational updates: added header height utilities and a leading-8 line-height for h2.
RSVP UI
apps/lfx-one/src/app/modules/meetings/components/rsvp-button-group/rsvp-button-group.component.html
Simplified disabled state to only block during loading; removed several non-selected styling variants.
Meeting Card / Join UI
apps/lfx-one/src/app/modules/meetings/components/meeting-card/meeting-card.component.html, apps/lfx-one/src/app/modules/meetings/components/meeting-card/meeting-card.component.ts, apps/lfx-one/src/app/modules/meetings/meeting-join/meeting-join.component.html, apps/lfx-one/src/app/modules/meetings/meeting-join/meeting-join.component.ts
Added isInvited and canRegisterForMeeting computed signals, conditional rendering to show RSVP or Register button, and registerForMeeting() to open the public registration dialog; wired refresh trigger to re-fetch meeting after registration.
Public Registration Modal (Client)
apps/lfx-one/src/app/modules/meetings/components/public-registration-modal/public-registration-modal.component.html, ...public-registration-modal.component.ts
New standalone modal component with reactive form (first_name, last_name, email, job_title, org_name), validation, submission handler that calls client service and returns registrant on success.
Client Meeting Service
apps/lfx-one/src/app/shared/services/meeting.service.ts
Added registerForPublicMeeting() to POST registrant data to /public/api/meetings/register.
Shared Types
packages/shared/src/interfaces/meeting.interface.ts
Added response-only invited: boolean field to Meeting interface.
Server: Helpers
apps/lfx-one/src/server/helpers/meeting.helper.ts
New helpers: isUserInvitedToMeeting(), addInvitedStatusToMeeting(), addInvitedStatusToMeetings() — compute and annotate meetings with invited using registrant lookups (M2M token optional).
Server: Meeting Service
apps/lfx-one/src/server/services/meeting.service.ts
getMeetingRegistrantsByEmail() now accepts optional m2mToken; added addMeetingRegistrantWithM2M() to create registrants using M2M auth — note: method appears declared twice in file.
Server: Controllers
apps/lfx-one/src/server/controllers/meeting.controller.ts, apps/lfx-one/src/server/controllers/public-meeting.controller.ts
Meeting responses enriched with invited via helpers; getMeetingById and other flows reuse an M2M token where appropriate; new registerForPublicMeeting() endpoint implemented in PublicMeetingController that creates a registrant using M2M.
Server: Routes
apps/lfx-one/src/server/routes/public-meetings.route.ts
Added public POST /register route wired to PublicMeetingController.registerForPublicMeeting().
Minor Content
apps/lfx-one/src/app/modules/committees/committee-view/committee-view.component.html
Link label changed from showing URL to static "Visit" text (href unchanged).

Sequence Diagram(s)

sequenceDiagram
    participant User as User (Unauthenticated)
    participant UI as Meeting UI
    participant Modal as PublicRegistrationModal
    participant Client as Frontend Service
    participant Server as API Server
    participant Service as Meeting Service (microservice)

    User->>UI: View public meeting (not invited)
    UI->>UI: evaluate canRegisterForMeeting
    UI->>User: Show "Register for Meeting" button
    User->>UI: Click Register
    UI->>Modal: Open PublicRegistrationModal
    Modal->>User: Collect form (first_name, last_name, email, ...)
    User->>Modal: Submit form
    Modal->>Client: registerForPublicMeeting(registrantData)
    Client->>Server: POST /public/api/meetings/register
    Server->>Service: addMeetingRegistrantWithM2M(data, m2mToken)
    Service-->>Server: Created MeetingRegistrant
    Server-->>Client: 200 OK (registrant)
    Client->>Modal: Return success
    Modal-->>UI: Close with registrant result
    UI->>UI: Increment registrant count, trigger refresh
    UI->>Server: Fetch updated meeting (may reuse m2mToken)
    Server-->>UI: Updated meeting (includes invited status)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

  • Pay attention to the duplicate addMeetingRegistrantWithM2M() declaration in apps/lfx-one/src/server/services/meeting.service.ts.
  • Verify M2M token generation/reuse and expiry handling across public-meeting.controller.ts, meeting.helper.ts, and meeting.service.ts.
  • Confirm invited computation correctness (email casing, empty email handling, performance for many meetings).
  • Validate dialog integration and that refreshTrigger$ reliably updates UI state after registration.

Possibly related PRs

Suggested labels

deploy-preview

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately summarizes the main features: public meeting registration and RSVP updates, matching the core changes in the changeset.
Description check ✅ Passed The PR description comprehensively relates to the changeset, covering public registration, RSVP updates, dashboard alignment fixes, and M2M token support with JIRA ticket references.
Linked Issues check ✅ Passed Code changes fully implement LFXV2-862 (public registration with modal, backend endpoint, invited status), LFXV2-863 (RSVP button updates), and LFXV2-864 (dashboard header alignment).
Out of Scope Changes check ✅ Passed All changes are within scope of the three linked issues. Styling updates (h-8, leading-8), RSVP button modifications, and registration flow additions directly address the stated objectives.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4279af0 and 8193e21.

📒 Files selected for processing (4)
  • apps/lfx-one/src/app/modules/committees/committee-view/committee-view.component.html (1 hunks)
  • apps/lfx-one/src/app/modules/meetings/components/meeting-card/meeting-card.component.ts (4 hunks)
  • apps/lfx-one/src/server/controllers/public-meeting.controller.ts (5 hunks)
  • apps/lfx-one/src/server/helpers/meeting.helper.ts (1 hunks)

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (7)
packages/shared/src/interfaces/meeting.interface.ts (1)

147-148: Ensure invited is consistently populated across all Meeting responses

invited is added as a required boolean on Meeting but marked “response only”. That’s fine as long as every endpoint (including any that build Meeting objects server-side, and anything extending Meeting like PastMeeting) now sets invited explicitly (e.g., true/false), not by omission.

If there are any Meeting responses that don’t yet add this flag, consider either:

  • Updating those code paths to always include invited, or
  • Making the field optional (invited?: boolean) and treating undefined as false in consumers.

This avoids silent divergence between runtime payloads and the shared type.

apps/lfx-one/src/app/modules/meetings/meeting-join/meeting-join.component.html (1)

160-175: Invited vs register logic on join page looks correct; consider more specific test-id

The new branch:

  • Shows the RSVP button group only when isInvited() is true.
  • Falls back to a “Register for Meeting” button when canRegisterForMeeting() is true.

That cleanly matches the intended behavior for invited vs non-invited authenticated users on public, non-restricted meetings.

One minor suggestion: the data-testid="register-meeting-button" used here is also used in the meeting card template. For clearer, less fragile tests, consider a more specific name, e.g. meeting-join-register-meeting-button, and aligning the card’s button similarly.

apps/lfx-one/src/app/modules/meetings/components/meeting-card/meeting-card.component.html (1)

274-295: Meeting card invited/register behavior aligns with new flow; test-id could be more specific

This block correctly:

  • Shows the RSVP button group only for invited, non-legacy, non-past meetings.
  • Shows a centered “Register for Meeting” button when canRegisterForMeeting() is true, gated by !pastMeeting() so it doesn’t appear for historical cards.

That matches the public registration requirements and keeps legacy meetings disabled via the existing isLegacyMeeting() flag on the RSVP group.

As with the join template, you may want to rename data-testid="register-meeting-button" to something card-specific (e.g., meeting-card-register-meeting-button) to avoid selector collisions and make tests easier to scope.

apps/lfx-one/src/app/modules/meetings/components/public-registration-modal/public-registration-modal.component.html (1)

48-53: Consider adding validation messages for first_name and last_name fields.

Email validation messages are shown inline, but first_name and last_name (which also have required validators) don't have similar inline validation messages. This creates an inconsistent user experience.

If lfx-input-text doesn't handle validation display internally, consider adding validation messages similar to email:

@if (form.get('first_name')?.errors?.['required'] && form.get('first_name')?.touched && form.get('first_name')?.dirty) {
  <p class="text-sm text-red-500">First name is required</p>
}
apps/lfx-one/src/app/modules/meetings/components/public-registration-modal/public-registration-modal.component.ts (1)

62-103: Submission logic is well-guarded but consider takeUntilDestroyed.

The onSubmit method properly guards against double submission with submitting() check. However, the subscription doesn't unsubscribe on component destruction, which could cause issues if the dialog closes during an in-flight request.

Consider using takeUntilDestroyed or a similar pattern to clean up the subscription:

+import { DestroyRef, inject } from '@angular/core';
+import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

export class PublicRegistrationModalComponent {
+  private readonly destroyRef = inject(DestroyRef);
   // ... other code

   public onSubmit(): void {
     // ... validation check
     this.meetingService
       .registerForPublicMeeting({...})
+      .pipe(takeUntilDestroyed(this.destroyRef))
       .subscribe({...});
   }
}
apps/lfx-one/src/server/helpers/meeting.helper.ts (1)

20-29: Consider adding structured logging for observability.

Per coding guidelines, backend services should use Pino for structured JSON logs. Adding DEBUG-level logging for the invitation check operation would help with troubleshooting and monitoring.

 export async function isUserInvitedToMeeting(req: Request, meetingUid: string, email: string, m2mToken?: string): Promise<boolean> {
   if (!email || !meetingUid) {
     return false;
   }

   const token = m2mToken || (await generateM2MToken(req));
+  req.log.debug({ operation: 'is_user_invited_to_meeting', meeting_uid: meetingUid }, 'Checking if user is invited to meeting');
   const registrants = await meetingService.getMeetingRegistrantsByEmail(req, meetingUid, email, token);

   return registrants.resources.length > 0;
 }
apps/lfx-one/src/server/services/meeting.service.ts (1)

1249-1286: Minor: Simplify header property syntax.

The implementation is correct. One minor style note: the computed property syntax ['X-Sync'] on line 1261 is unusual when the key is a valid identifier.

-        { Authorization: `Bearer ${m2mToken}`, ['X-Sync']: 'true' }
+        { Authorization: `Bearer ${m2mToken}`, 'X-Sync': 'true' }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between fddfe3d and 4279af0.

📒 Files selected for processing (17)
  • apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.html (1 hunks)
  • apps/lfx-one/src/app/modules/dashboards/components/pending-actions/pending-actions.component.html (1 hunks)
  • apps/lfx-one/src/app/modules/meetings/components/meeting-card/meeting-card.component.html (1 hunks)
  • apps/lfx-one/src/app/modules/meetings/components/meeting-card/meeting-card.component.ts (4 hunks)
  • apps/lfx-one/src/app/modules/meetings/components/public-registration-modal/public-registration-modal.component.html (1 hunks)
  • apps/lfx-one/src/app/modules/meetings/components/public-registration-modal/public-registration-modal.component.ts (1 hunks)
  • apps/lfx-one/src/app/modules/meetings/components/rsvp-button-group/rsvp-button-group.component.html (3 hunks)
  • apps/lfx-one/src/app/modules/meetings/meeting-join/meeting-join.component.html (1 hunks)
  • apps/lfx-one/src/app/modules/meetings/meeting-join/meeting-join.component.ts (9 hunks)
  • apps/lfx-one/src/app/shared/services/meeting.service.ts (1 hunks)
  • apps/lfx-one/src/server/controllers/meeting.controller.ts (5 hunks)
  • apps/lfx-one/src/server/controllers/public-meeting.controller.ts (5 hunks)
  • apps/lfx-one/src/server/helpers/meeting.helper.ts (1 hunks)
  • apps/lfx-one/src/server/routes/public-meetings.route.ts (1 hunks)
  • apps/lfx-one/src/server/services/meeting.service.ts (3 hunks)
  • apps/lfx-one/src/styles.scss (1 hunks)
  • packages/shared/src/interfaces/meeting.interface.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{css,scss}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Tailwind CSS with PrimeUI plugin and LFX custom colors for styling

Files:

  • apps/lfx-one/src/styles.scss
**/*.{html,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always add data-testid attributes when creating new components for reliable test targeting

Files:

  • apps/lfx-one/src/app/modules/dashboards/components/pending-actions/pending-actions.component.html
  • packages/shared/src/interfaces/meeting.interface.ts
  • apps/lfx-one/src/app/modules/meetings/components/public-registration-modal/public-registration-modal.component.html
  • apps/lfx-one/src/app/modules/meetings/components/public-registration-modal/public-registration-modal.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.html
  • apps/lfx-one/src/app/modules/meetings/components/meeting-card/meeting-card.component.html
  • apps/lfx-one/src/app/modules/meetings/meeting-join/meeting-join.component.ts
  • apps/lfx-one/src/server/helpers/meeting.helper.ts
  • apps/lfx-one/src/app/modules/meetings/components/rsvp-button-group/rsvp-button-group.component.html
  • apps/lfx-one/src/app/modules/meetings/components/meeting-card/meeting-card.component.ts
  • apps/lfx-one/src/app/modules/meetings/meeting-join/meeting-join.component.html
  • apps/lfx-one/src/server/routes/public-meetings.route.ts
  • apps/lfx-one/src/server/controllers/public-meeting.controller.ts
  • apps/lfx-one/src/server/services/meeting.service.ts
  • apps/lfx-one/src/server/controllers/meeting.controller.ts
  • apps/lfx-one/src/app/shared/services/meeting.service.ts
**/*.html

📄 CodeRabbit inference engine (CLAUDE.md)

Use data-testid naming convention - [section]-[component]-[element] for hierarchical structure

Files:

  • apps/lfx-one/src/app/modules/dashboards/components/pending-actions/pending-actions.component.html
  • apps/lfx-one/src/app/modules/meetings/components/public-registration-modal/public-registration-modal.component.html
  • apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.html
  • apps/lfx-one/src/app/modules/meetings/components/meeting-card/meeting-card.component.html
  • apps/lfx-one/src/app/modules/meetings/components/rsvp-button-group/rsvp-button-group.component.html
  • apps/lfx-one/src/app/modules/meetings/meeting-join/meeting-join.component.html
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Always use direct imports for standalone components - no barrel exports
Use TypeScript interfaces instead of union types for better maintainability
Do not nest ternary expressions
Prefer interface for defining object shapes in TypeScript
Use path mappings and import aliases as configured in tsconfig.json for imports

Files:

  • packages/shared/src/interfaces/meeting.interface.ts
  • apps/lfx-one/src/app/modules/meetings/components/public-registration-modal/public-registration-modal.component.ts
  • apps/lfx-one/src/app/modules/meetings/meeting-join/meeting-join.component.ts
  • apps/lfx-one/src/server/helpers/meeting.helper.ts
  • apps/lfx-one/src/app/modules/meetings/components/meeting-card/meeting-card.component.ts
  • apps/lfx-one/src/server/routes/public-meetings.route.ts
  • apps/lfx-one/src/server/controllers/public-meeting.controller.ts
  • apps/lfx-one/src/server/services/meeting.service.ts
  • apps/lfx-one/src/server/controllers/meeting.controller.ts
  • apps/lfx-one/src/app/shared/services/meeting.service.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: License headers are required on all source files
Prepend 'Generated with Claude Code (https://claude.ai/code)' if assisted with the code

Files:

  • packages/shared/src/interfaces/meeting.interface.ts
  • apps/lfx-one/src/app/modules/meetings/components/public-registration-modal/public-registration-modal.component.ts
  • apps/lfx-one/src/app/modules/meetings/meeting-join/meeting-join.component.ts
  • apps/lfx-one/src/server/helpers/meeting.helper.ts
  • apps/lfx-one/src/app/modules/meetings/components/meeting-card/meeting-card.component.ts
  • apps/lfx-one/src/server/routes/public-meetings.route.ts
  • apps/lfx-one/src/server/controllers/public-meeting.controller.ts
  • apps/lfx-one/src/server/services/meeting.service.ts
  • apps/lfx-one/src/server/controllers/meeting.controller.ts
  • apps/lfx-one/src/app/shared/services/meeting.service.ts
packages/shared/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

All shared types, interfaces, and constants are centralized in @lfx-one/shared package

Files:

  • packages/shared/src/interfaces/meeting.interface.ts
**/*.component.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use Angular 19 zoneless change detection with signals for component state management

Files:

  • apps/lfx-one/src/app/modules/meetings/components/public-registration-modal/public-registration-modal.component.ts
  • apps/lfx-one/src/app/modules/meetings/meeting-join/meeting-join.component.ts
  • apps/lfx-one/src/app/modules/meetings/components/meeting-card/meeting-card.component.ts
apps/**/**/server/**/*.{ts,js}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/**/**/server/**/*.{ts,js}: Use Pino for structured JSON logs with sensitive data redaction in all backend services
Always use err field for errors to leverage Pino's error serializer - correct: req.log.error({ err: error, ...metadata }, 'message')
Log INFO level for business operation completions (created, updated, deleted) and successful data retrieval
Log WARN level for error conditions leading to exceptions and data quality issues
Log DEBUG level for internal operations, preparation steps, and intent statements
Log ERROR level for system failures, unhandled exceptions, and critical errors

Files:

  • apps/lfx-one/src/server/helpers/meeting.helper.ts
  • apps/lfx-one/src/server/routes/public-meetings.route.ts
  • apps/lfx-one/src/server/controllers/public-meeting.controller.ts
  • apps/lfx-one/src/server/services/meeting.service.ts
  • apps/lfx-one/src/server/controllers/meeting.controller.ts
apps/**/**/server/**/*controller*.{ts,js}

📄 CodeRabbit inference engine (CLAUDE.md)

All controller functions must use Logger helper methods: Logger.start(), Logger.success(), Logger.error(), Logger.warning(), Logger.validation()

Files:

  • apps/lfx-one/src/server/controllers/public-meeting.controller.ts
  • apps/lfx-one/src/server/controllers/meeting.controller.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Agent
🔇 Additional comments (24)
apps/lfx-one/src/styles.scss (1)

31-33: h2 line-height change aligns with header height usage

Adding leading-8 to the default h2 style matches the new h-8 header containers and should improve visual alignment without affecting elements that already set their own typography classes. Looks good.

apps/lfx-one/src/app/modules/dashboards/components/pending-actions/pending-actions.component.html (1)

6-11: Header layout tweaks are consistent and non-breaking

Using h-8 on the header container and removing vertical padding from the <h2> aligns this header with the My Meetings header and the global h2 leading-8 change. No functional concerns.

apps/lfx-one/src/app/shared/services/meeting.service.ts (1)

481-489: Public registration service method is consistent and PII-safe

registerForPublicMeeting mirrors existing HTTP patterns (take(1) + catchError + rethrow) and targets the correct /public/api/meetings/register endpoint. Logging only meeting_uid avoids leaking registrant PII in console logs. No issues.

apps/lfx-one/src/server/routes/public-meetings.route.ts (1)

11-13: New /register public route is correctly wired

POST /register is added ahead of the /:id route, avoiding route conflicts, and delegates cleanly to PublicMeetingController.registerForPublicMeeting. The route shape matches the intended /public/api/meetings/register endpoint when mounted. No further changes needed here.

apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.html (1)

6-10: My Meetings header height change is consistent with dashboard layout

Adding h-8 to the header container brings this header in line with Pending Actions and the updated h2 line-height, improving cross-widget alignment without affecting behavior.

apps/lfx-one/src/app/modules/meetings/components/rsvp-button-group/rsvp-button-group.component.html (1)

33-44: LGTM! RSVP button now allows response changes.

The simplified disabled logic (only checking isLoading()) correctly enables users to change their RSVP after initial selection per LFXV2-863. The consistent styling for non-selected states with hover feedback is appropriate.

apps/lfx-one/src/app/modules/meetings/components/meeting-card/meeting-card.component.ts (2)

139-143: LGTM! Registration eligibility signals are well-designed.

The computed signals isInvited and canRegisterForMeeting correctly derive registration eligibility reactively. The logic !isInvited && !restricted && visibility === 'public' aligns with the PR requirement to allow registration only for non-invited users on public, non-restricted meetings.


263-287: Registration dialog integration looks correct.

The registerForMeeting() method properly opens the modal with meeting context, handles the success result by incrementing registrant count and refreshing the meeting data.

Note: The modal returns { registered: true, registrant } on success, but you're only checking result?.registered. Consider typing the result more precisely if you need the registrant data in the future.

apps/lfx-one/src/app/modules/meetings/components/public-registration-modal/public-registration-modal.component.html (2)

4-7: LGTM! Form structure and data-testid attributes are well-implemented.

The form properly binds to the FormGroup, has appropriate data-testid attributes following the naming convention, and displays meeting context to the user.


95-102: LGTM! Submit button correctly handles loading and validation states.

The button properly shows loading state via submitting() signal and is disabled when form is invalid or during submission, preventing duplicate submissions.

apps/lfx-one/src/server/controllers/meeting.controller.ts (3)

48-53: LGTM! Good defensive check for missing organizer.

Short-circuiting the registrant count fetch when no organizer is present prevents unnecessary API calls for invalid meeting states.


65-72: Invited status enrichment looks correct.

The implementation properly extracts user email with a fallback to empty string for unauthenticated users, and enriches meetings with invited status before sending to the client.

Minor observation: Line 69 uses invitedMeetings.forEach to mutate the array in place with count data. This works correctly since you're modifying properties on existing objects, not replacing them.


161-166: LGTM! Single meeting invited status enrichment.

Consistent with the list endpoint, the single meeting fetch also enriches the response with invited status for the current user.

apps/lfx-one/src/app/modules/meetings/components/public-registration-modal/public-registration-modal.component.ts (2)

35-60: Form initialization and user data pre-population look correct.

The form setup with appropriate validators and the logic to pre-populate from various user data sources (name, given_name/first_name, family_name/last_name) is well-implemented.


90-98: Error handling extracts message appropriately.

The error handler correctly attempts to extract the API error message with a sensible fallback. The submitting state is properly reset on both success and error paths.

apps/lfx-one/src/server/helpers/meeting.helper.ts (1)

56-63: LGTM! Efficient M2M token reuse pattern.

The implementation correctly generates a single M2M token and reuses it across all parallel meeting checks, avoiding redundant token generation.

apps/lfx-one/src/server/services/meeting.service.ts (1)

364-417: LGTM! Clean M2M token integration.

The optional m2mToken parameter with conditional Authorization header is well-implemented. The backward compatibility is maintained since existing callers without the token will continue to work.

apps/lfx-one/src/app/modules/meetings/meeting-join/meeting-join.component.ts (3)

180-204: LGTM! Clean modal integration with proper subscription handling.

The registration flow correctly:

  • Opens the modal with appropriate configuration
  • Uses take(1) to auto-unsubscribe after first emission
  • Triggers data refresh only on successful registration

551-560: LGTM! Signals correctly implement registration eligibility logic.

The computed signals properly derive:

  • isInvited: from meeting's invited property with nullish coalescing
  • canRegisterForMeeting: combines not-invited, not-restricted, and public visibility checks

68-71: DialogService is already provided at root level.

DialogService is configured in app.config.ts, so it's available globally and the component will function correctly without adding it to the local providers array. The empty providers: [] does not cause an error.

apps/lfx-one/src/server/controllers/public-meeting.controller.ts (4)

55-60: LGTM! Efficient M2M token reuse and invited status enrichment.

Good improvements:

  • M2M token is generated once and passed through to subsequent operations
  • Invited status is correctly computed only for authenticated users
  • Defaults to invited: false for unauthenticated requests

Also applies to: 98-104


289-307: LGTM! Comprehensive input validation.

The validation properly checks required fields (email, first_name, last_name) with appropriate error messages using ServiceValidationError.fromFieldErrors.


323-353: LGTM! Proper authorization guards for public meeting registration.

The endpoint correctly validates:

  1. Meeting must be public (visibility check)
  2. Meeting must not be restricted

Both checks use AuthorizationError with appropriate error messages and logging.


432-443: LGTM! Clean refactor for M2M token reuse.

The updated fetchMeetingWithM2M now accepts an optional pre-generated token, avoiding redundant token generation while maintaining backward compatibility for callers that don't provide a token.

Copy link
Contributor

Copilot AI left a comment

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 adds public meeting self-registration functionality for non-invited users, enables RSVP response changes after initial selection, and includes minor UI alignment fixes. The implementation includes a new public registration endpoint with M2M token authentication, frontend modal components for registration, and server-side invitation status checks to conditionally show registration or RSVP buttons.

Key Changes

  • Public Meeting Registration: New POST endpoint /public/api/meetings/register with validation for public, non-restricted meetings, plus frontend modal with form validation
  • RSVP Flexibility: Removed disabled state for already-selected RSVP buttons, allowing users to change their response
  • Invitation Status Tracking: Added invited boolean field to Meeting interface with helper functions to check user invitation status via registrant lookup

Reviewed changes

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

Show a summary per file
File Description
packages/shared/src/interfaces/meeting.interface.ts Adds invited boolean field to Meeting interface for response-only invitation status
apps/lfx-one/src/styles.scss Adds leading-8 to h2 elements for consistent header height alignment
apps/lfx-one/src/server/services/meeting.service.ts Adds M2M token parameter to getMeetingRegistrantsByEmail and new addMeetingRegistrantWithM2M method
apps/lfx-one/src/server/routes/public-meetings.route.ts Registers new POST /register route for public meeting registration
apps/lfx-one/src/server/helpers/meeting.helper.ts New helper file with functions to check invitation status and add invited field to meetings
apps/lfx-one/src/server/controllers/public-meeting.controller.ts Implements registerForPublicMeeting endpoint with validation and adds invited status to public meeting retrieval
apps/lfx-one/src/server/controllers/meeting.controller.ts Adds invitation status checks to authenticated meeting endpoints and handles meetings without organizers
apps/lfx-one/src/app/shared/services/meeting.service.ts Adds registerForPublicMeeting method to call public registration endpoint
apps/lfx-one/src/app/modules/meetings/meeting-join/meeting-join.component.ts Adds registration modal trigger, refresh logic, and computed signals for invitation status
apps/lfx-one/src/app/modules/meetings/meeting-join/meeting-join.component.html Shows registration button for non-invited users on public meetings
apps/lfx-one/src/app/modules/meetings/components/rsvp-button-group/rsvp-button-group.component.html Removes disabled states to allow RSVP response changes
apps/lfx-one/src/app/modules/meetings/components/public-registration-modal/* New modal component with form validation for public meeting registration
apps/lfx-one/src/app/modules/meetings/components/meeting-card/meeting-card.component.ts Adds registration modal trigger and refresh logic for invited status updates
apps/lfx-one/src/app/modules/meetings/components/meeting-card/meeting-card.component.html Conditionally shows registration button for non-invited users
apps/lfx-one/src/app/modules/dashboards/components/pending-actions/pending-actions.component.html Adds h-8 class to header for consistent height alignment
apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.html Adds h-8 class to header for consistent height alignment

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

LFXV2-862

- Skip invited status check for meeting organizers in helper
- Fix potential null reference in meeting card effect
- Remove PII (email) from public registration logs
- Change committee website link text to "Visit"

Signed-off-by: Asitha de Silva <asithade@gmail.com>
@asithade asithade merged commit 3e44a7b into main Dec 3, 2025
5 of 6 checks passed
@asithade asithade deleted the feat/LFXV2-862 branch December 3, 2025 22:09
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