-
Notifications
You must be signed in to change notification settings - Fork 0
feat(meetings): add meeting rsvp functionality with dynamic count calculation #135
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Add comprehensive RSVP support for meetings with the following features: - RSVP buttons (Yes/Maybe/No) in meeting cards with visual state - Scope selection modal for recurring meetings (this/all/following) - Dynamic RSVP count calculation respecting scope rules and recency - Backend endpoints for creating and fetching RSVPs - RSVP calculator utility handling occurrence-specific logic - Token-based user identification (backend derives user from bearer token) The RSVP system correctly handles: - Non-recurring meetings with simple scope - Recurring meetings with occurrence-specific RSVPs - Precedence by most recent applicable RSVP per user - Scope types: 'this' (single), 'all' (entire series), 'following' (future) Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Andres Tobon <andrest2455@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Conventional Commits FTW!
|
Note Other AI code review bot(s) detectedCodeRabbit 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. WalkthroughThis PR adds RSVP functionality to meeting management, including new API endpoints for creating meeting RSVPs and retrieving user and meeting RSVPs. New types and interfaces define RSVP responses and scope patterns. The getMeetingRegistrants method now optionally enriches registrants with associated RSVP data through an include_rsvp flag. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Component
participant Controller
participant MeetingService as Backend Service
participant API as Microservice
Client->>Component: Load meeting registrants
Component->>Controller: GET /meetings/:uid/registrants?include_rsvp=true
Controller->>MeetingService: getMeetingRegistrants(uid, includeRsvp=true)
MeetingService->>API: GET /registrants
API-->>MeetingService: registrants[]
alt includeRsvp is true
MeetingService->>API: GET /rsvps?filter=meeting_uid
API-->>MeetingService: rsvps[]
MeetingService->>MeetingService: Enrich registrants with RSVP by username
end
MeetingService-->>Controller: registrants[]{rsvp?}
Controller-->>Component: 200 registrants[]{rsvp?}
Component-->>Client: Display registrants with RSVP data
sequenceDiagram
participant Client
participant Component
participant Controller
participant MeetingService as Backend Service
participant API as Microservice
Client->>Component: Submit RSVP for meeting
Component->>Controller: POST /meetings/:uid/rsvp {scope, response}
Controller->>MeetingService: createMeetingRsvp(uid, payload, userContext)
MeetingService->>API: POST /meetings/:uid/rsvp
API-->>MeetingService: MeetingRsvp
MeetingService-->>Controller: MeetingRsvp
Controller-->>Component: 201 MeetingRsvp
Component-->>Client: RSVP confirmed
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
🚀 Deployment StatusYour branch has been deployed to: https://ui-pr-135.dev.v2.cluster.linuxfound.info Deployment Details:
The deployment will be automatically removed when this PR is closed. |
There was a problem hiding this 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 implements a new RSVP (Répondez s'il vous plaît) feature for meetings, allowing users to respond to meeting invitations with "accepted," "maybe," or "declined" status. The feature supports both non-recurring and recurring meetings with scope-based responses.
Key changes:
- Added RSVP data models and API endpoints for creating and retrieving RSVPs
- Implemented RSVP calculation logic that handles different scopes (this, all, following) for recurring meetings
- Enhanced the UI with RSVP buttons in meeting cards and a scope selection modal for recurring meetings
Reviewed Changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
packages/shared/src/interfaces/meeting.interface.ts |
Added RSVP-related TypeScript interfaces and types |
packages/shared/src/utils/rsvp-calculator.util.ts |
New utility for calculating RSVP counts based on scope and occurrence |
packages/shared/src/utils/index.ts |
Exported the new RSVP calculator utility |
apps/lfx-one/src/server/services/meeting.service.ts |
Added three new methods for RSVP operations (create, get user RSVP, get all RSVPs) |
apps/lfx-one/src/server/controllers/meeting.controller.ts |
Added controller methods for RSVP API endpoints |
apps/lfx-one/src/server/routes/meetings.route.ts |
Added three new RSVP routes |
apps/lfx-one/src/server/utils/auth-helper.ts |
Enhanced username extraction logic for better token handling |
apps/lfx-one/src/server/server.ts |
Added debug console.log and removed auth scope |
apps/lfx-one/src/app/shared/services/meeting.service.ts |
Added client-side service methods for RSVP operations |
apps/lfx-one/src/app/shared/components/rsvp-scope-modal/* |
New modal component for selecting RSVP scope |
apps/lfx-one/src/app/shared/components/dashboard-meeting-card/* |
Added RSVP UI buttons and interaction logic |
apps/lfx-one/src/app/modules/project/meetings/components/meeting-card/* |
Integrated RSVP counts display using the calculator utility |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
🧹 Nitpick comments (2)
apps/lfx-one/src/server/utils/auth-helper.ts (1)
27-28: Consider breaking the long line for improved readability.Line 28 is quite long (~170+ characters) and contains repeated property access patterns. While functionally correct, it could be more readable if split across multiple lines or refactored.
Optional refactor example:
- // For non-authelia tokens, try LFX SSO claim first - return req.oidc?.user?.['https://sso.linuxfoundation.org/claims/username'] || req.oidc?.user?.['username'] || req.oidc?.user?.['preferred_username'] || null; + // For non-authelia tokens, try LFX SSO claim first + const user = req.oidc?.user; + return ( + user?.['https://sso.linuxfoundation.org/claims/username'] || + user?.['username'] || + user?.['preferred_username'] || + null + );apps/lfx-one/src/server/services/meeting.service.ts (1)
698-702: Consider logging errors before returning null.The catch block at Lines 698-701 silently returns
nullfor any error, which could hide important issues like network failures, authorization problems, or malformed responses. While returningnullmay be appropriate for a "not found" case, other errors should be visible.Consider enhancing the error handling to distinguish between expected "not found" cases and unexpected errors:
} catch (error) { Logger.error(req, 'get_user_meeting_rsvp', Date.now(), error); + // Consider: Only return null for specific "not found" errors + // Re-throw or handle other error types appropriately return null; }The same consideration applies to
getMeetingRsvps(Lines 731-733) which returns an empty array on any error.
📜 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.
📒 Files selected for processing (15)
apps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.html(2 hunks)apps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.ts(5 hunks)apps/lfx-one/src/app/shared/components/dashboard-meeting-card/dashboard-meeting-card.component.html(1 hunks)apps/lfx-one/src/app/shared/components/dashboard-meeting-card/dashboard-meeting-card.component.ts(4 hunks)apps/lfx-one/src/app/shared/components/rsvp-scope-modal/rsvp-scope-modal.component.html(1 hunks)apps/lfx-one/src/app/shared/components/rsvp-scope-modal/rsvp-scope-modal.component.ts(1 hunks)apps/lfx-one/src/app/shared/services/meeting.service.ts(2 hunks)apps/lfx-one/src/server/controllers/meeting.controller.ts(2 hunks)apps/lfx-one/src/server/routes/meetings.route.ts(1 hunks)apps/lfx-one/src/server/server.ts(2 hunks)apps/lfx-one/src/server/services/meeting.service.ts(2 hunks)apps/lfx-one/src/server/utils/auth-helper.ts(1 hunks)packages/shared/src/interfaces/meeting.interface.ts(1 hunks)packages/shared/src/utils/index.ts(1 hunks)packages/shared/src/utils/rsvp-calculator.util.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
apps/lfx-one/src/**/*.html
📄 CodeRabbit inference engine (CLAUDE.md)
apps/lfx-one/src/**/*.html: Always add data-testid attributes when creating new Angular components for reliable test targeting
Use data-testid naming convention [section]-[component]-[element]
Files:
apps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.htmlapps/lfx-one/src/app/shared/components/rsvp-scope-modal/rsvp-scope-modal.component.htmlapps/lfx-one/src/app/shared/components/dashboard-meeting-card/dashboard-meeting-card.component.html
**/*.{ts,tsx,js,jsx,mjs,cjs,html,css,scss}
📄 CodeRabbit inference engine (CLAUDE.md)
Include required license headers on all source files
Files:
apps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.htmlpackages/shared/src/utils/index.tsapps/lfx-one/src/server/utils/auth-helper.tsapps/lfx-one/src/server/server.tsapps/lfx-one/src/server/routes/meetings.route.tspackages/shared/src/interfaces/meeting.interface.tsapps/lfx-one/src/app/shared/components/rsvp-scope-modal/rsvp-scope-modal.component.htmlapps/lfx-one/src/server/controllers/meeting.controller.tspackages/shared/src/utils/rsvp-calculator.util.tsapps/lfx-one/src/app/shared/components/dashboard-meeting-card/dashboard-meeting-card.component.htmlapps/lfx-one/src/app/shared/components/rsvp-scope-modal/rsvp-scope-modal.component.tsapps/lfx-one/src/app/shared/services/meeting.service.tsapps/lfx-one/src/app/shared/components/dashboard-meeting-card/dashboard-meeting-card.component.tsapps/lfx-one/src/server/services/meeting.service.tsapps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.ts
**/index.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Do not use barrel exports; always use direct imports for standalone components
Files:
packages/shared/src/utils/index.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use TypeScript interfaces instead of union types for better maintainability
When defining PrimeNG-related types, reference the official PrimeNG component interfaces
Files:
packages/shared/src/utils/index.tsapps/lfx-one/src/server/utils/auth-helper.tsapps/lfx-one/src/server/server.tsapps/lfx-one/src/server/routes/meetings.route.tspackages/shared/src/interfaces/meeting.interface.tsapps/lfx-one/src/server/controllers/meeting.controller.tspackages/shared/src/utils/rsvp-calculator.util.tsapps/lfx-one/src/app/shared/components/rsvp-scope-modal/rsvp-scope-modal.component.tsapps/lfx-one/src/app/shared/services/meeting.service.tsapps/lfx-one/src/app/shared/components/dashboard-meeting-card/dashboard-meeting-card.component.tsapps/lfx-one/src/server/services/meeting.service.tsapps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Do not nest ternary expressions
Files:
packages/shared/src/utils/index.tsapps/lfx-one/src/server/utils/auth-helper.tsapps/lfx-one/src/server/server.tsapps/lfx-one/src/server/routes/meetings.route.tspackages/shared/src/interfaces/meeting.interface.tsapps/lfx-one/src/server/controllers/meeting.controller.tspackages/shared/src/utils/rsvp-calculator.util.tsapps/lfx-one/src/app/shared/components/rsvp-scope-modal/rsvp-scope-modal.component.tsapps/lfx-one/src/app/shared/services/meeting.service.tsapps/lfx-one/src/app/shared/components/dashboard-meeting-card/dashboard-meeting-card.component.tsapps/lfx-one/src/server/services/meeting.service.tsapps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.ts
packages/shared/src/interfaces/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Place all TypeScript interfaces in the shared package at packages/shared/src/interfaces
Files:
packages/shared/src/interfaces/meeting.interface.ts
🧬 Code graph analysis (7)
apps/lfx-one/src/server/controllers/meeting.controller.ts (2)
packages/shared/src/interfaces/meeting.interface.ts (1)
CreateMeetingRsvpRequest(732-743)apps/lfx-one/src/server/errors/index.ts (1)
ServiceValidationError(7-7)
packages/shared/src/utils/rsvp-calculator.util.ts (1)
packages/shared/src/interfaces/meeting.interface.ts (2)
MeetingOccurrence(156-167)MeetingRsvp(707-726)
apps/lfx-one/src/app/shared/components/rsvp-scope-modal/rsvp-scope-modal.component.ts (2)
packages/shared/src/interfaces/meeting.interface.ts (1)
RsvpScope(701-701)apps/lfx-one/src/app/shared/components/dashboard-meeting-card/dashboard-meeting-card.component.ts (1)
Component(23-247)
apps/lfx-one/src/app/shared/services/meeting.service.ts (1)
packages/shared/src/interfaces/meeting.interface.ts (2)
CreateMeetingRsvpRequest(732-743)MeetingRsvp(707-726)
apps/lfx-one/src/app/shared/components/dashboard-meeting-card/dashboard-meeting-card.component.ts (1)
packages/shared/src/interfaces/meeting.interface.ts (6)
Meeting(75-150)MeetingOccurrence(156-167)MeetingRsvp(707-726)RsvpResponse(695-695)RsvpScope(701-701)CreateMeetingRsvpRequest(732-743)
apps/lfx-one/src/server/services/meeting.service.ts (3)
packages/shared/src/interfaces/meeting.interface.ts (2)
CreateMeetingRsvpRequest(732-743)MeetingRsvp(707-726)apps/lfx-one/src/server/utils/auth-helper.ts (1)
getUsernameFromAuth(10-29)packages/shared/src/interfaces/api.interface.ts (1)
QueryServiceResponse(58-61)
apps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.ts (2)
packages/shared/src/interfaces/meeting.interface.ts (1)
MeetingRsvp(707-726)packages/shared/src/utils/rsvp-calculator.util.ts (2)
RsvpCounts(6-11)calculateRsvpCounts(22-67)
⏰ 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: build-and-push
🔇 Additional comments (21)
apps/lfx-one/src/server/server.ts (1)
164-164: Scope removal is safe and appropriate for this codebase architecture.After verifying the codebase, the removal of
update:current_user_metadatafrom the OIDC authorization scope is safe and does not break existing functionality.The application uses a NATS-based microservices architecture where:
- The OIDC access token is used only as a bearer token for authentication between frontend and backend
- The backend auth-service handles all user metadata updates via NATS messaging
- Backend uses machine-to-machine (M2M) credentials for any Auth0 Management API calls, not the user's OIDC token
No code in the repository calls Auth0 Management API directly using the OIDC access token, so this scope was never required. Removing it follows security best practice by requesting only necessary scopes.
packages/shared/src/interfaces/meeting.interface.ts (1)
690-743: LGTM! Well-structured RSVP types with clear documentation.The RSVP type definitions are clean and well-documented. The use of union types for
RsvpResponseandRsvpScopeis appropriate for string literal enums. The optional fields inCreateMeetingRsvpRequestcorrectly align with the backend design where user context is derived from the bearer token.packages/shared/src/utils/index.ts (1)
8-8: LGTM! Follows the existing barrel export pattern for utilities.This addition is consistent with the other utility exports in this file. The coding guideline about barrel exports specifically applies to Angular standalone components, not shared utility modules.
apps/lfx-one/src/app/shared/components/rsvp-scope-modal/rsvp-scope-modal.component.ts (1)
1-59: LGTM! Clean modal component implementation.The component is well-structured with proper use of Angular signals and DynamicDialogRef. The scope options are clearly defined with descriptive labels and the modal flow is straightforward.
apps/lfx-one/src/app/shared/components/dashboard-meeting-card/dashboard-meeting-card.component.html (1)
94-141: LGTM! Well-implemented RSVP UI with proper accessibility and state management.The RSVP section is cleanly integrated with:
- Clear visual feedback for the current user's RSVP state
- Proper loading state handling
- All required data-testid attributes for testing
- Good UX with compact inline buttons
apps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.html (1)
150-180: LGTM! Consistent migration to computed RSVP counts.The changes correctly replace direct registrant count references with the computed
rsvpCounts()values, ensuring the UI reflects the new RSVP-based calculation logic.apps/lfx-one/src/server/routes/meetings.route.ts (1)
51-54: LGTM! RESTful RSVP routes properly integrated.The three RSVP endpoints follow the existing routing patterns and RESTful conventions. The routes are clearly grouped with a comment header for maintainability.
apps/lfx-one/src/app/shared/components/rsvp-scope-modal/rsvp-scope-modal.component.html (1)
1-30: LGTM! Well-structured modal template with good accessibility.The template is clean and accessible with:
- Proper button types and disabled states
- Comprehensive data-testid attributes for testing
- Clear visual feedback for option selection
- Appropriate use of Angular control flow (@for)
apps/lfx-one/src/server/services/meeting.service.ts (1)
635-735: LGTM! RSVP service methods well-structured with proper logging.The three RSVP methods follow the established patterns in this service with appropriate use of the microservice proxy and comprehensive logging. The
createMeetingRsvpmethod correctly strips optional fields to let the backend derive user context from the bearer token.apps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.ts (2)
623-637: LGTM: Well-structured RSVP count calculation.The method correctly:
- Returns zero counts for past meetings
- Delegates to the shared
calculateRsvpCountsutility for upcoming meetings- Passes all required parameters (occurrence, RSVPs, meeting start time)
639-652: LGTM: Correctly integrated RSVP counts.The attendance percentage calculation now uses
rsvpCounts().acceptedinstead of the deprecatedmeeting.registrants_accepted_count, properly reflecting the new RSVP-based counting system.apps/lfx-one/src/app/shared/services/meeting.service.ts (3)
443-451: LGTM: Consistent implementation.The
createMeetingRsvpmethod follows the established patterns in this service:
- Uses
take(1)for single emission- Proper error handling with
catchErrorandthrowError- Console logging for debugging
453-460: LGTM: Appropriate error handling for optional data.The
getUserMeetingRsvpmethod correctly returnsnullon error usingof(null), which is appropriate for optional user RSVP data that may not exist.
462-469: LGTM: Appropriate error handling for list operations.The
getMeetingRsvpsmethod correctly returns an empty array on error usingof([]), which is appropriate for list operations and prevents downstream null-pointer issues.packages/shared/src/utils/rsvp-calculator.util.ts (1)
22-67: LGTM: Well-structured RSVP counting logic.The function correctly:
- Groups RSVPs by username to handle multiple responses per user
- Uses
getApplicableRsvpto determine which RSVP applies to each occurrence- Counts responses by type (accepted/declined/maybe)
- Handles empty RSVP lists gracefully
apps/lfx-one/src/server/controllers/meeting.controller.ts (3)
727-781: LGTM: Well-structured controller method.The
createMeetingRsvpmethod follows controller best practices:
- Validates required parameters (UID, response, scope)
- Uses consistent logging (start, success, error)
- Proper error handling with next(error)
- Follows established patterns in this controller
783-819: LGTM: Consistent with existing patterns.The
getUserMeetingRsvpmethod follows the established controller patterns with proper validation, logging, and error handling.
821-853: LGTM: Proper list endpoint implementation.The
getMeetingRsvpsmethod correctly:
- Validates the meeting UID
- Logs the count of RSVPs returned
- Follows error handling conventions
apps/lfx-one/src/app/shared/components/dashboard-meeting-card/dashboard-meeting-card.component.ts (3)
194-212: LGTM: Proper modal flow for recurring meetings.The method correctly:
- Shows scope selection modal for recurring meetings
- Uses 'this' scope directly for non-recurring meetings
- Handles the modal response appropriately
Note: The
onClosesubscription is automatically cleaned up by PrimeNG'sDialogServicewhen the dialog closes.
214-246: LGTM: Well-implemented RSVP submission.The method properly:
- Manages loading state
- Provides user feedback via MessageService
- Handles errors gracefully with fallback
- Updates local state on success
156-158: LGTM: Clean recurring meeting detection.The computed signal correctly determines if a meeting is recurring by checking if
recurrenceis not null.
apps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.ts
Outdated
Show resolved
Hide resolved
...lfx-one/src/app/shared/components/dashboard-meeting-card/dashboard-meeting-card.component.ts
Outdated
Show resolved
Hide resolved
…roach - Convert RSVP loading from effects to signal-based reactive streams - Add occurrence_id field to MeetingRsvp interface for proper typing - Improve RSVP matching logic with case-insensitive comparison - Add RSVP refresh coordination across occurrence cards - Display meeting context in RSVP scope modal - Change RsvpScope from 'this' to 'single' for clarity - Fix nested ternary expression for better readability - Remove debug console.log statements - Add username validation in getUserMeetingRsvp 🤖 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Andres Tobon <andrest2455@gmail.com>
… into andrest50/meeting-rsvp
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (1)
apps/lfx-one/src/app/shared/components/dashboard-meeting-card/dashboard-meeting-card.component.ts (1)
187-212: Memory leak: Manual subscription still needs cleanup.The subscription created in the constructor lacks cleanup, causing a memory leak when the component is destroyed. Although this is no longer in an effect (as flagged in the previous review), the subscription still persists without a cleanup mechanism.
Apply this fix:
+import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; + public constructor() { // Convert meeting input signal to observable and create reactive attachment stream const meeting$ = toObservable(this.meeting); const attachments$ = meeting$.pipe( switchMap((meeting) => { if (meeting.uid) { return this.meetingService.getMeetingAttachments(meeting.uid).pipe(catchError(() => of([]))); } return of([]); }) ); this.attachments = toSignal(attachments$, { initialValue: [] }); // Create signal-based RSVP loading that handles both initial load and refresh const rsvpFetchTrigger = computed(() => ({ meetingUid: this.meeting()?.uid, refreshTrigger: this.refreshTrigger(), skipUid: this.skipRefreshMeetingUid(), })); const rsvpFetchTrigger$ = toObservable(rsvpFetchTrigger); // Fetch RSVP when meeting changes or when explicitly refreshed rsvpFetchTrigger$ .pipe( + takeUntilDestroyed(), map(({ meetingUid, refreshTrigger, skipUid }) => { // Don't fetch if no meeting if (!meetingUid) return null; // Skip refresh if this is the card that was just updated (but allow initial load) if (refreshTrigger > 0 && skipUid && meetingUid === skipUid) { return null; } // Return meeting UID to trigger fetch (include refreshTrigger to force new fetch on refresh) return refreshTrigger === 0 ? meetingUid : `${meetingUid}-${refreshTrigger}`; }), switchMap((fetchKey) => { if (!fetchKey) return of(null); // Extract meeting UID (handle both "uid" and "uid-trigger" formats) const meetingUid = fetchKey.split('-')[0]; return this.meetingService.getUserMeetingRsvp(meetingUid).pipe(catchError(() => of(null))); }) ) .subscribe((rsvp) => { this.userRsvp.set(rsvp); }); }
📜 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.
📒 Files selected for processing (15)
apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.html(2 hunks)apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.ts(3 hunks)apps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.ts(6 hunks)apps/lfx-one/src/app/modules/project/meetings/components/meeting-registrants/meeting-registrants.component.ts(2 hunks)apps/lfx-one/src/app/modules/project/meetings/components/registrant-card/registrant-card.component.html(1 hunks)apps/lfx-one/src/app/modules/project/meetings/components/registrant-card/registrant-card.component.ts(2 hunks)apps/lfx-one/src/app/shared/components/dashboard-meeting-card/dashboard-meeting-card.component.ts(4 hunks)apps/lfx-one/src/app/shared/components/rsvp-scope-modal/rsvp-scope-modal.component.html(1 hunks)apps/lfx-one/src/app/shared/components/rsvp-scope-modal/rsvp-scope-modal.component.ts(1 hunks)apps/lfx-one/src/server/controllers/meeting.controller.ts(2 hunks)apps/lfx-one/src/server/server.ts(1 hunks)apps/lfx-one/src/server/services/meeting.service.ts(2 hunks)packages/shared/src/interfaces/meeting.interface.ts(2 hunks)packages/shared/src/utils/index.ts(1 hunks)packages/shared/src/utils/rsvp-calculator.util.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- apps/lfx-one/src/server/server.ts
- apps/lfx-one/src/app/shared/components/rsvp-scope-modal/rsvp-scope-modal.component.html
- packages/shared/src/utils/index.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use TypeScript interfaces instead of union types for better maintainability
When defining PrimeNG-related types, reference the official PrimeNG component interfaces
Files:
apps/lfx-one/src/app/modules/project/meetings/components/registrant-card/registrant-card.component.tsapps/lfx-one/src/server/services/meeting.service.tspackages/shared/src/utils/rsvp-calculator.util.tsapps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.tsapps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.tsapps/lfx-one/src/app/shared/components/rsvp-scope-modal/rsvp-scope-modal.component.tsapps/lfx-one/src/app/modules/project/meetings/components/meeting-registrants/meeting-registrants.component.tsapps/lfx-one/src/app/shared/components/dashboard-meeting-card/dashboard-meeting-card.component.tspackages/shared/src/interfaces/meeting.interface.tsapps/lfx-one/src/server/controllers/meeting.controller.ts
**/*.{ts,tsx,js,jsx,mjs,cjs,html,css,scss}
📄 CodeRabbit inference engine (CLAUDE.md)
Include required license headers on all source files
Files:
apps/lfx-one/src/app/modules/project/meetings/components/registrant-card/registrant-card.component.tsapps/lfx-one/src/app/modules/project/meetings/components/registrant-card/registrant-card.component.htmlapps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.htmlapps/lfx-one/src/server/services/meeting.service.tspackages/shared/src/utils/rsvp-calculator.util.tsapps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.tsapps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.tsapps/lfx-one/src/app/shared/components/rsvp-scope-modal/rsvp-scope-modal.component.tsapps/lfx-one/src/app/modules/project/meetings/components/meeting-registrants/meeting-registrants.component.tsapps/lfx-one/src/app/shared/components/dashboard-meeting-card/dashboard-meeting-card.component.tspackages/shared/src/interfaces/meeting.interface.tsapps/lfx-one/src/server/controllers/meeting.controller.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Do not nest ternary expressions
Files:
apps/lfx-one/src/app/modules/project/meetings/components/registrant-card/registrant-card.component.tsapps/lfx-one/src/server/services/meeting.service.tspackages/shared/src/utils/rsvp-calculator.util.tsapps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.tsapps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.tsapps/lfx-one/src/app/shared/components/rsvp-scope-modal/rsvp-scope-modal.component.tsapps/lfx-one/src/app/modules/project/meetings/components/meeting-registrants/meeting-registrants.component.tsapps/lfx-one/src/app/shared/components/dashboard-meeting-card/dashboard-meeting-card.component.tspackages/shared/src/interfaces/meeting.interface.tsapps/lfx-one/src/server/controllers/meeting.controller.ts
apps/lfx-one/src/**/*.html
📄 CodeRabbit inference engine (CLAUDE.md)
apps/lfx-one/src/**/*.html: Always add data-testid attributes when creating new Angular components for reliable test targeting
Use data-testid naming convention [section]-[component]-[element]
Files:
apps/lfx-one/src/app/modules/project/meetings/components/registrant-card/registrant-card.component.htmlapps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.html
packages/shared/src/interfaces/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Place all TypeScript interfaces in the shared package at packages/shared/src/interfaces
Files:
packages/shared/src/interfaces/meeting.interface.ts
🧠 Learnings (3)
📚 Learning: 2025-10-21T21:19:13.599Z
Learnt from: andrest50
PR: linuxfoundation/lfx-v2-ui#125
File: apps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.ts:345-350
Timestamp: 2025-10-21T21:19:13.599Z
Learning: In the Angular meeting card component (apps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.ts), when selecting between `summary.summary_data.edited_content` and `summary.summary_data.content`, the logical OR operator (`||`) is intentionally used instead of nullish coalescing (`??`) because empty string edited_content should fall back to the original content rather than being displayed as empty.
Applied to files:
apps/lfx-one/src/app/modules/project/meetings/components/registrant-card/registrant-card.component.htmlapps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.htmlapps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.tsapps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.tsapps/lfx-one/src/app/shared/components/rsvp-scope-modal/rsvp-scope-modal.component.tsapps/lfx-one/src/app/shared/components/dashboard-meeting-card/dashboard-meeting-card.component.ts
📚 Learning: 2025-09-16T03:32:46.518Z
Learnt from: CR
PR: linuxfoundation/lfx-v2-ui#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-16T03:32:46.518Z
Learning: Applies to **/*.{ts,tsx} : When defining PrimeNG-related types, reference the official PrimeNG component interfaces
Applied to files:
apps/lfx-one/src/app/modules/project/meetings/components/meeting-registrants/meeting-registrants.component.ts
📚 Learning: 2025-09-16T03:32:46.518Z
Learnt from: CR
PR: linuxfoundation/lfx-v2-ui#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-16T03:32:46.518Z
Learning: All PrimeNG components are wrapped in LFX components to keep UI library independence
Applied to files:
apps/lfx-one/src/app/modules/project/meetings/components/meeting-registrants/meeting-registrants.component.ts
🧬 Code graph analysis (8)
apps/lfx-one/src/server/services/meeting.service.ts (3)
packages/shared/src/interfaces/meeting.interface.ts (2)
CreateMeetingRsvpRequest(736-747)MeetingRsvp(709-730)apps/lfx-one/src/server/utils/auth-helper.ts (1)
getUsernameFromAuth(10-22)packages/shared/src/interfaces/api.interface.ts (1)
QueryServiceResponse(58-61)
packages/shared/src/utils/rsvp-calculator.util.ts (1)
packages/shared/src/interfaces/meeting.interface.ts (2)
MeetingOccurrence(156-167)MeetingRsvp(709-730)
apps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.ts (2)
packages/shared/src/interfaces/meeting.interface.ts (1)
MeetingRsvp(709-730)packages/shared/src/utils/rsvp-calculator.util.ts (2)
RsvpCounts(6-11)calculateRsvpCounts(22-67)
apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.ts (1)
packages/shared/src/interfaces/meeting.interface.ts (2)
RsvpResponse(697-697)RsvpScope(703-703)
apps/lfx-one/src/app/shared/components/rsvp-scope-modal/rsvp-scope-modal.component.ts (2)
packages/shared/src/interfaces/meeting.interface.ts (3)
RsvpScope(703-703)Meeting(75-150)MeetingOccurrence(156-167)apps/lfx-one/src/app/shared/components/dashboard-meeting-card/dashboard-meeting-card.component.ts (1)
Component(23-278)
apps/lfx-one/src/app/modules/project/meetings/components/meeting-registrants/meeting-registrants.component.ts (2)
packages/shared/src/interfaces/meeting.interface.ts (4)
MeetingRegistrant(262-303)RegistrantState(362-362)MeetingRegistrantWithState(368-377)MeetingRsvp(709-730)packages/shared/src/utils/form.utils.ts (1)
generateTempId(9-11)
apps/lfx-one/src/app/shared/components/dashboard-meeting-card/dashboard-meeting-card.component.ts (1)
packages/shared/src/interfaces/meeting.interface.ts (6)
Meeting(75-150)MeetingOccurrence(156-167)RsvpResponse(697-697)RsvpScope(703-703)MeetingRsvp(709-730)CreateMeetingRsvpRequest(736-747)
apps/lfx-one/src/server/controllers/meeting.controller.ts (1)
packages/shared/src/interfaces/meeting.interface.ts (1)
CreateMeetingRsvpRequest(736-747)
apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.ts
Outdated
Show resolved
Hide resolved
...lfx-one/src/app/shared/components/dashboard-meeting-card/dashboard-meeting-card.component.ts
Outdated
Show resolved
Hide resolved
- Change skip logic from meetingUid to composite key (meetingUid:occurrenceId) - Fix RSVP refresh to only skip the specific occurrence that was clicked - Update event payload to include occurrenceId - Fix rsvpFetchTrigger to return structured data instead of string concatenation - Prevent UUID corruption from hyphen-based string splitting This ensures all occurrence cards properly update their RSVP status when scope is "all" or "following", except the specific occurrence that was just clicked. 🤖 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Andres Tobon <andrest2455@gmail.com>
| state: state, | ||
| originalData: state === 'existing' ? { ...registrant } : undefined, | ||
| tempId: state === 'new' ? generateTempId() : undefined, | ||
| rsvpStatus: rsvpStatus as any, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this casted as any?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed it to rsvpStatus: rsvpStatus as RsvpResponse | undefined unless that isn't what you meant.
| // Clear the skip UID after a short delay | ||
| setTimeout(() => { | ||
| this.skipRefreshMeetingUid.set(null); | ||
| }, 1000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we have to wait for a set time to clear this?
| <lfx-badge value="Invited" severity="secondary" size="small" styleClass="inline-flex items-center"> </lfx-badge> | ||
| <!-- RSVP status badge (invited/accepted/declined/maybe) --> | ||
| <lfx-badge [value]="rsvpBadge().label" [severity]="rsvpBadge().severity" size="small" styleClass="inline-flex items-center gap-1"> | ||
| <i [class]="rsvpBadge().icon + ' text-xs'"></i> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use ngClass instead of string concat
| type="button" | ||
| (click)="handleRsvpClick('accepted')" | ||
| [disabled]="rsvpLoading()" | ||
| [class]=" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use ngclass - we want to avoid the use of js in the html as much as possible and using ternary operations isn't ideal
| type="button" | ||
| (click)="handleRsvpClick('maybe')" | ||
| [disabled]="rsvpLoading()" | ||
| [class]=" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use ngclass
| interface ScopeOption { | ||
| value: RsvpScope; | ||
| label: string; | ||
| description: string; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interface should live in shared package
| templateUrl: './rsvp-scope-modal.component.html', | ||
| }) | ||
| export class RsvpScopeModalComponent { | ||
| private readonly ref = inject(DynamicDialogRef); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename to dialogRef
| { | ||
| value: 'single', | ||
| label: 'This occurrence only', | ||
| description: 'Apply this RSVP to only this specific meeting', | ||
| }, | ||
| { | ||
| value: 'all', | ||
| label: 'All occurrences', | ||
| description: 'Apply this RSVP to all occurrences in the series', | ||
| }, | ||
| { | ||
| value: 'following', | ||
| label: 'This and following occurrences', | ||
| description: 'Apply this RSVP to this meeting and all future occurrences', | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move to shared constants for meetings
| }), | ||
| }) | ||
| ), | ||
| rsvps: this.meetingService.getMeetingRsvps(uid).pipe( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can chat about it in our call, but wondering if this is best done on the getMeetingRegistrants as a flag to call and mapping server side as well instead of duplicating the call here and calculating/mapping client side.
| export interface RsvpCounts { | ||
| accepted: number; | ||
| declined: number; | ||
| maybe: number; | ||
| total: number; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interface should live in the interfaces folder
…ed package - Move RsvpScopeOption interface to shared package for reusability - Move RsvpCounts interface to shared package interfaces - Add RSVP_SCOPE_OPTIONS constant to shared package constants - Update RSVP scope modal to use shared constants and interfaces - Update dashboard meeting card to use ngClass for RSVP buttons - Add pipe(take(1)) to dialog close subscription to prevent memory leaks - Remove unnecessary DialogService provider from dashboard meeting card - Simplify skip refresh key logic in my-meetings component - Fix max-len linting error in meeting-registrants component - Update registrant card to use ngClass for icon styling - Improve type safety with proper RsvpResponse type annotation Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Andres Tobon <andrest2455@gmail.com>
…nt fetching - Add optional include_rsvp query parameter to GET /meetings/:uid/registrants endpoint - Backend service fetches and attaches RSVP data when include_rsvp=true - Add optional rsvp field to MeetingRegistrant interface - Optimize meeting-registrants component to use single API call with include_rsvp=true - Remove forkJoin pattern and complex RSVP matching logic from frontend - Reduce code complexity by ~70 lines in meeting-registrants component - Improve performance by reducing network requests from 2 to 1 - Update frontend service to accept includeRsvp parameter (defaults to false) - Add comprehensive error handling and logging for RSVP fetching - Maintain backward compatibility with existing API consumers Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Andres Tobon <andrest2455@gmail.com>
Remove the ability to submit RSVPs directly from the dashboard meeting cards. This functionality will be refactored and reimplemented in a future iteration. Changes: - Remove RSVP buttons (Yes/Maybe/No) from dashboard meeting cards - Delete RSVP scope modal component - Remove RSVP submission handlers and refresh logic - Clean up unused inputs (refreshTrigger, skipRefreshKey) - Remove isRecurringMeeting computed signal - Add update:current_user_metadata scope for future user profile updates 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: Andres Tobon <andrest2455@gmail.com>
Remove the ability to submit RSVPs directly from the dashboard meeting cards. This functionality will be refactored and reimplemented in a future iteration. Changes: - Remove RSVP buttons (Yes/Maybe/No) from dashboard meeting cards - Delete RSVP scope modal component - Remove RSVP submission handlers and refresh logic - Clean up unused inputs (refreshTrigger, skipRefreshKey) - Remove isRecurringMeeting computed signal - Add update:current_user_metadata scope for future user profile updates 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: Andres Tobon <andrest2455@gmail.com>
… into andrest50/meeting-rsvp
Signed-off-by: Andres Tobon <andrest2455@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 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.
📒 Files selected for processing (1)
packages/shared/src/interfaces/meeting.interface.ts(3 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
packages/shared/src/interfaces/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Place all TypeScript interfaces in the shared package at packages/shared/src/interfaces
Files:
packages/shared/src/interfaces/meeting.interface.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use TypeScript interfaces instead of union types for better maintainability
When defining PrimeNG-related types, reference the official PrimeNG component interfaces
Files:
packages/shared/src/interfaces/meeting.interface.ts
**/*.{ts,tsx,js,jsx,mjs,cjs,html,css,scss}
📄 CodeRabbit inference engine (CLAUDE.md)
Include required license headers on all source files
Files:
packages/shared/src/interfaces/meeting.interface.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Do not nest ternary expressions
Files:
packages/shared/src/interfaces/meeting.interface.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: build-and-push
| /** | ||
| * Request payload for creating a meeting RSVP | ||
| * @description Data required to create or update a meeting RSVP | ||
| */ | ||
| export interface CreateMeetingRsvpRequest { | ||
| /** User's registrant ID (optional - backend derives from username if not provided) */ | ||
| registrant_id?: string; | ||
| /** User's username (optional - backend derives from authenticated user if not provided) */ | ||
| username?: string; | ||
| /** User's email (optional - backend derives from authenticated user if not provided) */ | ||
| email?: string; | ||
| /** Scope of the RSVP */ | ||
| scope: RsvpScope; | ||
| /** User's RSVP response */ | ||
| response: RsvpResponse; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add occurrence_id field for single-scope RSVPs.
When scope is 'single', the RSVP applies to a specific occurrence of a recurring meeting. However, this request interface lacks an occurrence_id field to specify which occurrence the user is RSVPing to. The response interface MeetingRsvp includes this field (line 742), but the request does not.
Apply this diff to add the missing field:
export interface CreateMeetingRsvpRequest {
/** User's registrant ID (optional - backend derives from username if not provided) */
registrant_id?: string;
/** User's username (optional - backend derives from authenticated user if not provided) */
username?: string;
/** User's email (optional - backend derives from authenticated user if not provided) */
email?: string;
/** Scope of the RSVP */
scope: RsvpScope;
/** User's RSVP response */
response: RsvpResponse;
+ /** Occurrence ID (required when scope is 'single') */
+ occurrence_id?: string;
}🤖 Prompt for AI Agents
In packages/shared/src/interfaces/meeting.interface.ts around lines 749 to 764,
the CreateMeetingRsvpRequest interface is missing an occurrence_id needed when
scope === 'single'; add an optional occurrence_id?: string field (with a brief
JSDoc comment like "ID of the specific occurrence when scope is 'single'") to
the interface so clients can target a specific meeting occurrence while keeping
it optional for other scopes.
🧹 Deployment RemovedThe deployment for PR #135 has been removed. |
Summary
Add comprehensive RSVP support for meetings with the following features:
Implementation Details
Frontend Changes
Backend Changes
/api/meetings/:uid/rsvp, GET/api/meetings/:uid/rsvp, GET/api/meetings/:uid/rsvpsShared Utilities
rsvp-calculator.util.tswith comprehensive scope handling logicRSVP System Logic
The RSVP system correctly handles:
this(single): Applies only to the selected occurrenceall(entire series): Applies to all occurrences in the seriesfollowing(future): Applies to this occurrence and all future onesTest Plan
Ticket
LFXV2-684
🤖 Generated with Claude Code