Skip to content

Conversation

@asithade
Copy link
Contributor

@asithade asithade commented Jan 12, 2026

Summary

  • Implement vote details drawer component for viewing and submitting votes
  • Add reusable selectable card component for vote options
  • Wire up votes table to open drawer on action button click

Changes

  • Vote Details Drawer: Slide-over panel showing full vote information

    • Vote title, committee badges, status tag, due date
    • Vote description, creator, and discussion link
    • Questions section with selectable card options
    • Warning/success messages based on vote state
    • Confirmation and success modals for submission flow
  • Selectable Card Component: Reusable shared component

    • Works with reactive forms (FormGroup/FormControl)
    • Supports radio-style (single) and checkbox-style (toggle) modes
    • Visual feedback with checkmark indicator
    • Keyboard accessible (Enter/Space)
  • Shared Interfaces: Added VoteDetails, PollQuestion, UserChoice, PollAnswer

JIRA

LFXV2-975

🤖 Generated with Claude Code

- Add vote-details-drawer component for viewing and submitting votes
- Add selectable-card shared component for vote options
- Support single-choice and multiple-choice questions
- Add confirmation and success modals for vote submission
- Add VoteDetails, PollQuestion, UserChoice interfaces to shared package
- Add mock poll questions and descriptions for development
- Wire up votes table to open drawer on action button click

LFXV2-975

Signed-off-by: Asitha de Silva <[email protected]>
Copilot AI review requested due to automatic review settings January 12, 2026 21:23
@asithade asithade requested a review from jordane as a code owner January 12, 2026 21:23
@coderabbitai
Copy link

coderabbitai bot commented Jan 12, 2026

Walkthrough

Adds a vote details drawer feature with UI, form-driven voting flow, selectable card control, poll interfaces/constants, and integration into the votes table; also includes several related UI and type refactors across activity, surveys, mailing-list, and shared interfaces.

Changes

Cohort / File(s) Summary
Vote Details Drawer & Dialogs
apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts, .../vote-details-drawer.component.html, .../vote-details-drawer.component.scss, .../vote-submitted-dialog/*
New VoteDetailsDrawerComponent with reactive forms, checkbox form sets, computed signals, confirmation and success dialogs; emits voteSubmitted payload. Includes templates and styles plus VoteSubmittedDialog component.
Votes Table Integration
apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts, .../votes-table.component.html
Wire-up to open drawer on vote click; added signals for drawerVisible and selectedVote; optimistic local modifications on voteSubmitted; mock enrichment helpers (getMockVoteDetails, getMockVoteCount, merged/local vote sources).
Selectable Card Component
apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.ts, .../selectable-card.component.html
New reusable lfx-selectable-card bound to FormGroup controls; supports value and toggle modes, keyboard activation, selected/disabled states and test IDs.
Poll Interfaces & Mock Data
packages/shared/src/interfaces/poll.interface.ts, packages/shared/src/constants/poll.constants.ts
New exported types: UserChoice, PollQuestion, PollAnswer, GenericChoiceVotes, VoteDetails (extends UserVote); added MOCK_POLL_QUESTIONS and MOCK_POLL_DESCRIPTIONS maps for local/demo data.
Shared Component Interfaces
packages/shared/src/interfaces/components.interface.ts
Added RelativeDateInfo and generic TabOption<T = string> interfaces.
Mailing-list / Committee type migration
apps/lfx-one/src/app/modules/mailing-lists/*, apps/lfx-one/src/app/shared/components/committee-selector/committee-selector.component.ts, packages/shared/src/interfaces/mailing-list.interface.ts
Removed deprecated MailingListCommittee alias; replaced usages with CommitteeReference across mailing-list components and committee-selector; adjusted imports and form/control typing.
Surveys, Activity and Top-bar Signal refactors
apps/lfx-one/src/app/modules/my-activity/components/surveys-table/surveys-table.component.ts, apps/lfx-one/src/app/modules/my-activity/components/activity-top-bar/activity-top-bar.component.ts, apps/lfx-one/src/app/modules/my-activity/my-activity-dashboard/my-activity-dashboard.component.ts
Converted several components to use Angular Signals: readonly inputs, signal-based option/filtered lists, moved constructor init logic into private initializers, strengthened typings (readonly, generic TabOption).
My Meetings — Scroll Shadow & Template restructure
apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.ts, .../my-meetings.component.html
Added ScrollShadowDirective import and ViewChild; restructured meetings template into a scrollable container with top/bottom shadow elements and adjusted empty-state placement.
Documentation
CLAUDE.md
Added component organization pattern and guidance documenting signals/model patterns and recommended component structure.

Sequence Diagram

sequenceDiagram
    participant User
    participant VotesTable
    participant VoteDetailsDrawer
    participant FormState as Form/State
    participant Modal as Confirmation Modal
    participant Dialog as Success Dialog

    User->>VotesTable: Click "Vote" action
    VotesTable->>VotesTable: onVoteClick(vote) -> set selectedVote, drawerVisible = true
    VotesTable->>VoteDetailsDrawer: render with VoteDetails input

    User->>VoteDetailsDrawer: Select option(s)
    VoteDetailsDrawer->>FormState: update voteForm / checkboxForms
    VoteDetailsDrawer->>VoteDetailsDrawer: compute allQuestionsAnswered

    User->>VoteDetailsDrawer: Click "Submit Vote"
    VoteDetailsDrawer->>Modal: open confirmation modal
    User->>Modal: Confirm
    Modal->>VoteDetailsDrawer: confirm result
    VoteDetailsDrawer->>VotesTable: emit voteSubmitted { pollId, answers }
    VotesTable->>VotesTable: onVoteSubmitted -> apply local modifications
    VoteDetailsDrawer->>Dialog: open success dialog
    User->>Dialog: Click "Done"
    Dialog->>VoteDetailsDrawer: close drawer
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Most changes align with LFXV2-975 objectives. However, the PR includes several unrelated changes: scroll shadow directive integration in my-meetings, MailingListCommittee/CommitteeReference type replacements in mailing-lists modules, surveys-table refactoring, activity-top-bar type updates, and TabOption/RelativeDateInfo shared interfaces that extend beyond the vote drawer scope. Separate out-of-scope changes (mailing-lists refactoring, my-meetings scroll shadow, surveys-table signal refactoring, TabOption/RelativeDateInfo interfaces) into distinct PRs focused on their respective features to maintain PR clarity and single-responsibility principle.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: implementing a vote details drawer with selectable cards for the my-activity feature.
Description check ✅ Passed The description provides a comprehensive summary of all major changes including vote details drawer, selectable card component, shared interfaces, and integration with votes table.
Linked Issues check ✅ Passed The implementation fulfills core LFXV2-975 requirements: vote details drawer with metadata display, selectable cards for single/multi-choice questions, submission flow with modals, and votes table integration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/LFXV2-975

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: 5

🤖 Fix all issues with AI agents
In
@apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.html:
- Line 65: In the template for VoteDetailsDrawer (the div with data-testid
"vote-details-drawer-content" in vote-details-drawer.component.html) fix the
Tailwind typo by replacing the invalid class "p6" with the correct "p-6" so the
padding utility applies correctly.

In
@apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts:
- Around line 62-75: The computed allQuestionsAnswered currently reads
non-reactive form values (this.voteForm.get(...).value) so it only recomputes
when this.vote() changes; make it reactive by introducing a signal (e.g.,
formValuesChanged) that you update on form value changes (subscribe to
this.voteForm.valueChanges inside the constructor/effect after buildForm) and
reference that signal at the top of allQuestionsAnswered so the computed re-runs
when the user updates controls; keep existing logic using vote(), voteForm, and
checkboxForms but ensure formValuesChanged() is touched inside the computed to
trigger recomputation.
- Line 32: Remove the unnecessary CUSTOM_ELEMENTS_SCHEMA from the @Component
decorator in VoteDetailsDrawerComponent (it’s redundant because the lfx-*
components are already declared/imported), and modify the template to wrap the
PrimeNG <p-drawer> in the LFX wrapper component (e.g., replace or nest p-drawer
inside the corresponding <lfx-drawer> wrapper component) ensuring the wrapper
component is present in the component's imports array; update any template
inputs/events to pass through the LFX wrapper so the original p-drawer behavior
is preserved.

In
@apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts:
- Around line 127-131: getVotesSource currently prefers internalVotes over the
votes input which can ignore parent updates after a submission; fix by syncing
internalVotes from the votes() input when the input changes—add an effect in the
component constructor that watches this.votes() and resets or merges into
this.internalVotes (or reinitialize internalVotes) when the incoming array
changes, or choose to always read from internalVotes but initialize it from
votes() so getVotesSource, internalVotes, and votes() remain consistent with
parent updates.

In
@apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.html:
- Around line 17-20: The selectable-card components use role="option" which
requires their parent container to have role="listbox" for proper ARIA
accessibility. Locate the parent div wrapping selectable-card components in
vote-details-drawer.component.html around lines 151 and 164, and add the
attribute role="listbox" to these container divs to establish the correct
accessibility hierarchy.
🧹 Nitpick comments (8)
apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.scss (1)

4-17: Consider alternatives to ::ng-deep for future-proofing.

::ng-deep is deprecated and may be removed in future Angular versions. While it still works, consider these alternatives:

  1. Use PrimeNG's styleClass input to apply classes directly
  2. Define global styles in a shared stylesheet (if these styles are reused)
  3. Use CSS custom properties for theming

For now, this approach is functional and acceptable for component-scoped overrides of third-party component styles.

packages/shared/src/interfaces/poll.interface.ts (1)

54-55: Consider using a string literal type or enum for type field.

The type field is typed as string but appears to have a known set of values ('single_choice', 'multiple_choice'). A more specific type would provide better type safety and IDE autocompletion.

♻️ Suggested improvement
// Option 1: String literal type
type QuestionType = 'single_choice' | 'multiple_choice';

// Then in PollQuestion:
type: QuestionType;

// Option 2: Add to existing enums in poll.enum.ts
export enum QuestionType {
  SINGLE_CHOICE = 'single_choice',
  MULTIPLE_CHOICE = 'multiple_choice',
}
apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.html (2)

17-22: Empty div used as a spacer.

Line 18 has an empty <div></div> used for spacing in a flexbox with justify-between. Consider using ml-auto on the button instead for cleaner markup.

♻️ Suggested refactor
-        <div class="flex items-center justify-between">
-          <div></div>
-          <button type="button" (click)="onClose()" class="text-slate-400 hover:text-slate-600 transition-colors p-1" data-testid="vote-details-drawer-close">
+        <div class="flex items-center">
+          <button type="button" (click)="onClose()" class="ml-auto text-slate-400 hover:text-slate-600 transition-colors p-1" data-testid="vote-details-drawer-close">

216-239: Consider extracting modals into reusable components.

The confirmation and success modals are implemented inline with manual backdrop handling and z-index management. Consider extracting these into reusable modal components (or using an existing LFX modal wrapper) to ensure consistent behavior across the app and reduce template complexity.

apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts (1)

133-167: Mock data implementation should be clearly marked for replacement.

The getMockVoteDetails method hardcodes mock data including descriptions, creator, and discussion links. While acceptable for development, add a TODO or consider injecting a service that can be swapped for real API calls.

♻️ Suggested improvement
+ // TODO: Replace with real API call to fetch vote details
+ // This mock implementation is for development purposes only
  private getMockVoteDetails(vote: UserVote): VoteDetails {
apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.ts (1)

96-99: Remove redundant identity map.

The .map((value) => value) on line 98 is a no-op and can be removed.

♻️ Proposed fix
          return formControl.valueChanges.pipe(
-           startWith(formControl.value),
-           map((value) => value)
+           startWith(formControl.value)
          );
apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts (2)

45-48: Consider using protected for template-bound properties.

voteForm and checkboxForms are only used in the template and should be marked as protected rather than public to limit their API surface. Based on Angular 19+ zoneless patterns with signals, this maintains encapsulation.

♻️ Proposed fix
  // Form for vote responses - dynamically built based on questions
- public voteForm = new FormGroup<Record<string, FormControl<string | null>>>({});
+ protected voteForm = new FormGroup<Record<string, FormControl<string | null>>>({});

  // Checkbox forms - one FormGroup per multi-choice question
- public checkboxForms: Record<string, FormGroup<Record<string, FormControl<boolean>>>> = {};
+ protected checkboxForms: Record<string, FormGroup<Record<string, FormControl<boolean>>>> = {};

51-52: Consider using signals for modal state consistency.

showConfirmModal and showSuccessModal are plain boolean properties while the rest of the component uses signals. For consistency with the zoneless change detection pattern mentioned in guidelines, consider using signals.

♻️ Suggested refactor
  // Modal states
- protected showConfirmModal = false;
- protected showSuccessModal = false;
+ protected readonly showConfirmModal = signal(false);
+ protected readonly showSuccessModal = signal(false);

Then update usages to use .set() and () accessor patterns.

📜 Review details

Configuration used: Organization 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 3cddbae and 77dd613.

📒 Files selected for processing (9)
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.html
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.scss
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.html
  • apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts
  • apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.html
  • apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.ts
  • packages/shared/src/constants/poll.constants.ts
  • packages/shared/src/interfaces/poll.interface.ts
🧰 Additional context used
📓 Path-based instructions (9)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use TypeScript interfaces instead of union types for better maintainability

Files:

  • packages/shared/src/constants/poll.constants.ts
  • packages/shared/src/interfaces/poll.interface.ts
  • apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts
packages/shared/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • packages/shared/src/constants/poll.constants.ts
  • packages/shared/src/interfaces/poll.interface.ts
{apps,packages}/**/*.{ts,tsx,js,html,scss,css}

📄 CodeRabbit inference engine (CLAUDE.md)

{apps,packages}/**/*.{ts,tsx,js,html,scss,css}: License headers are required on all source files
Always prepend 'Generated with Claude Code (https://claude.ai/code)' if Claude Code assisted with the code

Files:

  • packages/shared/src/constants/poll.constants.ts
  • apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.html
  • packages/shared/src/interfaces/poll.interface.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.scss
  • apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.html
  • apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.html
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js}: Do not nest ternary expressions
Always use async/await for promises in TypeScript/JavaScript
Use camelCase for variable and function names

Files:

  • packages/shared/src/constants/poll.constants.ts
  • packages/shared/src/interfaces/poll.interface.ts
  • apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts
apps/lfx-one/src/**/*.component.html

📄 CodeRabbit inference engine (CLAUDE.md)

apps/lfx-one/src/**/*.component.html: Use data-testid naming convention: [section]-[component]-[element] for hierarchical structure
Use flex + flex-col + gap-* instead of space-y-* for spacing

Files:

  • apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.html
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.html
  • apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.html
apps/lfx-one/**/*.{ts,tsx,html,scss,css}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Prettier configuration with Tailwind integration in lfx-one app

Files:

  • apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.html
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.scss
  • apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.html
  • apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.html
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts
apps/lfx-one/src/**/*.{html,scss,css}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Tailwind CSS with PrimeUI plugin and LFX colors for styling

Files:

  • apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.html
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.scss
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.html
  • apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.html
apps/lfx-one/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/lfx-one/src/**/*.{ts,tsx}: Use zoneless change detection with signals in Angular 19
Always use direct imports for standalone components - no barrel exports
Use ESLint and Prettier for code quality, following Angular-specific ESLint rules

Files:

  • apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts
apps/lfx-one/src/**/*.component.ts

📄 CodeRabbit inference engine (CLAUDE.md)

apps/lfx-one/src/**/*.component.ts: All PrimeNG components must be wrapped in LFX components for UI library independence
Add data-testid attributes when creating new components for reliable test targeting

Files:

  • apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts
🧠 Learnings (1)
📚 Learning: 2025-12-10T23:20:29.281Z
Learnt from: asithade
Repo: linuxfoundation/lfx-v2-ui PR: 206
File: apps/lfx-one/src/app/modules/meetings/components/agenda-template-selector/agenda-template-selector.component.ts:11-15
Timestamp: 2025-12-10T23:20:29.281Z
Learning: For Angular 19+ projects, standalone defaults to true for components, directives, and pipes when not explicitly declared. Therefore, components with an imports array in the Component decorator do not require an explicit standalone: true declaration to be standalone. In reviews, verify that a component without an explicit standalone flag and with an imports array will behave as a standalone component according to Angular 19+ defaults. If you need to enforce explicit standalone behavior for clarity or consistency, you can still add standalone: true, but it is not required by default in Angular 19+. Ensure this guidance is considered when reviewing any component.ts files across the codebase.

Applied to files:

  • apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts
🧬 Code graph analysis (2)
packages/shared/src/constants/poll.constants.ts (1)
packages/shared/src/interfaces/poll.interface.ts (1)
  • PollQuestion (49-60)
apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts (3)
packages/shared/src/interfaces/poll.interface.ts (3)
  • UserVote (11-28)
  • VoteDetails (93-114)
  • PollAnswer (66-79)
packages/shared/src/constants/poll.constants.ts (2)
  • MOCK_POLL_DESCRIPTIONS (167-173)
  • MOCK_POLL_QUESTIONS (73-161)
packages/shared/src/utils/poll.utils.ts (1)
  • CombinedVoteStatus (11-11)
⏰ 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 (16)
apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.html (2)

121-123: LGTM!

The drawer integration follows Angular best practices:

  • Two-way binding [(visible)] for drawer visibility state
  • Signal accessor selectedVote() for reactive data flow
  • Event binding for submission handling

The placement outside <lfx-card> is appropriate for a modal/drawer overlay component.


103-103: LGTM!

The click handler correctly passes the vote object to onVoteClick() to populate the drawer with vote details.

packages/shared/src/constants/poll.constants.ts (2)

73-161: LGTM! Mock data structure is well-organized.

The mock data correctly implements the PollQuestion interface with proper UserChoice objects. Good use of Map for efficient lookups by poll ID, and the JSDoc comments clearly indicate this is temporary mock data.

Consider adding a TODO comment or tracking issue for removing/replacing this mock data when the API is ready.


167-173: LGTM!

Descriptions are properly keyed to match the poll IDs in MOCK_POLL_QUESTIONS, ensuring data consistency.

apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.html (1)

4-22: LGTM! Well-structured accessible component.

The implementation correctly handles:

  • Visual states (selected, disabled, hover) via ngClass
  • Keyboard accessibility with tabindex management
  • ARIA attributes for screen reader support
  • Event binding for click and keyboard interactions
packages/shared/src/interfaces/poll.interface.ts (2)

66-79: LGTM!

The PollAnswer interface clearly separates regular selections (user_choice) from ranked choices (ranked_user_choice), supporting both plurality and ranked-choice voting scenarios. The required arrays ensure consistent structure even when one is empty.


93-114: LGTM!

The VoteDetails interface is well-designed:

  • Extends UserVote to inherit base poll properties
  • Required poll_questions ensures questions are always available for rendering
  • Optional poll_answers correctly models the "not yet voted" state
  • Clear JSDoc documentation for each property
apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.html (2)

130-177: Well-structured question rendering with proper accessibility.

The questions section properly handles both single and multiple choice types, uses track for performance, and includes appropriate data-testid attributes for testing. The conditional rendering for empty choices is a good defensive pattern.


4-11: No action required. The project architecture explicitly supports direct PrimeNG component integration as documented in the README. Direct usage of p-drawer is consistent with the project's stated approach and is used throughout the codebase, including in shared components like data-copilot.

apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts (2)

100-104: Good implementation of drawer opening flow.

The onVoteClick method correctly constructs vote details and updates the drawer state signals in the proper order.


110-125: Vote submission handler updates state correctly.

The onVoteSubmitted method properly updates the vote status to RESPONDED and sets the creation time. The immutable update pattern using map is correct.

apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.ts (3)

86-104: Complex RxJS chain for control value tracking.

The initControlValue method uses toObservableswitchMaptoSignal to track the form control's value. This approach handles dynamic form/control changes well, but consider documenting why this pattern is needed versus a simpler approach.


59-76: Well-implemented click handler with proper guard clauses.

The onCardClick method correctly checks for disabled state, handles both toggle and value modes, and has proper null checks for the form control.


78-84: Good keyboard accessibility implementation.

The onKeydown handler properly supports Enter and Space keys for activation, and correctly prevents default behavior to avoid page scrolling on Space.

apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts (2)

145-153: Effect-based form building is appropriate.

Using effect() to rebuild the form when the vote input changes is the correct Angular signals pattern for side effects that need to react to signal changes.


223-270: Well-structured form building with pre-population support.

The buildForm method correctly handles both single and multiple choice questions, pre-populates values from existing answers, and properly disables forms in read-only mode.

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 implements a comprehensive vote details drawer with selectable card components, enabling users to view vote information and submit their responses through an interactive UI.

Changes:

  • Added vote details drawer component with vote submission flow including confirmation and success modals
  • Implemented reusable selectable card component supporting both single-choice (radio) and multiple-choice (checkbox) selection modes
  • Extended shared interfaces to support detailed vote data including questions, choices, and answers

Reviewed changes

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

Show a summary per file
File Description
packages/shared/src/interfaces/poll.interface.ts Added VoteDetails, PollQuestion, PollAnswer, UserChoice interfaces for vote drawer functionality
packages/shared/src/constants/poll.constants.ts Added mock poll questions and descriptions for testing the vote drawer
apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.ts New reusable component for selectable cards with form integration
apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.html Template for selectable card with accessibility attributes
apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts Wired up vote click handler, drawer state management, and vote submission logic
apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.html Added vote details drawer component reference
apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts Core drawer logic with form building, validation, and submission handling
apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.html Drawer UI with question display, selection options, and modal dialogs
apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.scss Drawer styling with custom PrimeNG drawer overrides

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

- Add VoteSubmittedDialogComponent loaded via DialogService
- Fix votes-table to use local modifications pattern for proper input sync
- Use PollStatus enum in template instead of string literal
- Handle mixed question types in selectionTypeText
- Show vote choice text only for single-question polls
- Update Your Vote section for multi-question polls

LFXV2-975

Signed-off-by: Asitha de Silva <[email protected]>
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: 1

🤖 Fix all issues with AI agents
In
@apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts:
- Around line 103-112: onVoteSubmitted currently only saves vote_status and
vote_creation_time into the localModifications map, so the submitted answers are
not preserved; update the handler in votes-table.component (onVoteSubmitted) to
also store result.answers as poll_answers when setting modifications for
result.pollId, and update the localModifications map type (or UserVote
interface) to allow poll_answers: PollAnswer[] (e.g., Map<string,
Partial<UserVote & { poll_answers?: PollAnswer[] }>> or extend UserVote) so the
drawer will render the actual submitted answers before parent sync.
🧹 Nitpick comments (6)
apps/lfx-one/src/app/modules/my-activity/my-activity-dashboard/my-activity-dashboard.component.ts (1)

42-44: Consider simplifying to inline computed signals.

The private initializer methods add indirection without providing additional value. The standard Angular pattern for simple computed signals is more concise and equally readable:

protected readonly isVotesTab = computed(() => this.activeTab() === 'votes');
protected readonly isSurveysTab = computed(() => this.activeTab() === 'surveys');

The computed() function already returns Signal<T>, so explicit typing is redundant, and the initializer methods don't add testability or reusability benefits for these one-liners.

♻️ Suggested simplification
-  // === Computed Signals ===
-  protected readonly isVotesTab: Signal<boolean> = this.initIsVotesTab();
-  protected readonly isSurveysTab: Signal<boolean> = this.initIsSurveysTab();
+  // === Computed Signals ===
+  protected readonly isVotesTab = computed(() => this.activeTab() === 'votes');
+  protected readonly isSurveysTab = computed(() => this.activeTab() === 'surveys');

And remove the initializer methods (lines 51-58).

Also applies to: 51-58

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

30-30: Consider using overflow-auto instead of overflow-scroll.

overflow-scroll always renders scrollbars regardless of content height (on some browsers/OS), whereas overflow-auto only shows scrollbars when content overflows. This would provide a cleaner appearance when the content fits within max-h-[30rem].

Suggested change
-        <div lfxScrollShadow class="flex flex-col gap-1 overflow-scroll max-h-[30rem] py-1 -mt-1" data-testid="dashboard-my-meetings-list">
+        <div lfxScrollShadow class="flex flex-col gap-1 overflow-auto max-h-[30rem] py-1 -mt-1" data-testid="dashboard-my-meetings-list">
apps/lfx-one/src/app/modules/my-activity/components/vote-submitted-dialog/vote-submitted-dialog.component.html (1)

6-10: Consider extracting inline SVG to an icon component.

The inline SVG checkmark works correctly, but for consistency with the rest of the codebase, consider using a FontAwesome icon class (e.g., fa-light fa-check) or an existing icon component if available.

💡 Optional: Use FontAwesome icon
-  <div class="w-12 h-12 bg-green-100 rounded-full flex items-center justify-center mx-auto">
-    <svg class="w-6 h-6 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
-      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
-    </svg>
-  </div>
+  <div class="w-12 h-12 bg-green-100 rounded-full flex items-center justify-center mx-auto">
+    <i class="fa-light fa-check text-xl text-green-600"></i>
+  </div>
apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts (1)

239-258: Consider improving the "0 days left" edge case.

When diffDays is 0 (poll closes today), the text displays "0 days left" which may confuse users. Consider using "Closes today" for better clarity.

💡 Suggested improvement
       if (diffDays < 0) {
         return { text: 'Closed', color: 'text-slate-500' };
+      } else if (diffDays === 0) {
+        return { text: 'Closes today', color: 'text-red-600' };
       } else if (diffDays <= 3) {
         return { text: `${diffDays} day${diffDays !== 1 ? 's' : ''} left`, color: 'text-red-600' };
apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts (2)

73-74: Consider clearing localModifications when parent input updates.

The localModifications map accumulates optimistic updates but is never cleared. If the parent component eventually syncs the actual vote data (e.g., after an API call), the stale modifications will persist and continue to be merged unnecessarily. Consider implementing a mechanism to clear modifications when the parent's votes input updates with the synced data.

♻️ Suggested approach using effect to clear stale modifications
import { effect } from '@angular/core';

// In constructor or field initializer:
constructor() {
  // Clear local modifications when input votes change (parent synced)
  effect(() => {
    const inputVotes = this.votes();
    const modifications = this.localModifications();
    
    if (modifications.size > 0) {
      // Check if any modified vote now has the expected status in input
      const updatedModifications = new Map(modifications);
      let hasChanges = false;
      
      for (const [pollId, mod] of modifications) {
        const inputVote = inputVotes.find(v => v.poll_id === pollId);
        if (inputVote && inputVote.vote_status === mod.vote_status) {
          updatedModifications.delete(pollId);
          hasChanges = true;
        }
      }
      
      if (hasChanges) {
        this.localModifications.set(updatedModifications);
      }
    }
  });
}

115-125: Non-null assertion on form control access.

Using this.searchForm.get('search')! assumes the control always exists. While safe here since the form is defined in the same class, consider using a direct reference for better type safety.

♻️ Alternative using direct control reference
 public searchForm = new FormGroup({
   search: new FormControl<string>(''),
   status: new FormControl<CombinedVoteStatus | null>(null),
   committee: new FormControl<string | null>(null),
 });
+
+// Direct reference for type safety
+private readonly searchControl = this.searchForm.controls.search;

 private initSearchTerm(): Signal<string> {
   return toSignal(
-    this.searchForm.get('search')!.valueChanges.pipe(
+    this.searchControl.valueChanges.pipe(
       startWith(''),
       debounceTime(300),
       distinctUntilChanged(),
       map((value) => value ?? '')
     ),
     { initialValue: '' }
   );
 }
📜 Review details

Configuration used: Organization 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 77dd613 and 0727308.

📒 Files selected for processing (18)
  • CLAUDE.md
  • apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.html
  • apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.ts
  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-dashboard/mailing-list-dashboard.component.ts
  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-manage/mailing-list-manage.component.ts
  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-view/mailing-list-view.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/activity-top-bar/activity-top-bar.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/surveys-table/surveys-table.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.html
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-submitted-dialog/vote-submitted-dialog.component.html
  • apps/lfx-one/src/app/modules/my-activity/components/vote-submitted-dialog/vote-submitted-dialog.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts
  • apps/lfx-one/src/app/modules/my-activity/my-activity-dashboard/my-activity-dashboard.component.ts
  • apps/lfx-one/src/app/shared/components/committee-selector/committee-selector.component.ts
  • apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.ts
  • packages/shared/src/interfaces/components.interface.ts
  • packages/shared/src/interfaces/mailing-list.interface.ts
💤 Files with no reviewable changes (1)
  • packages/shared/src/interfaces/mailing-list.interface.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.html
  • apps/lfx-one/src/app/shared/components/selectable-card/selectable-card.component.ts
🧰 Additional context used
📓 Path-based instructions (9)
apps/lfx-one/src/**/*.component.html

📄 CodeRabbit inference engine (CLAUDE.md)

apps/lfx-one/src/**/*.component.html: Use data-testid naming convention: [section]-[component]-[element] for hierarchical structure
Use flex + flex-col + gap-* instead of space-y-* for spacing

Files:

  • apps/lfx-one/src/app/modules/my-activity/components/vote-submitted-dialog/vote-submitted-dialog.component.html
  • apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.html
{apps,packages}/**/*.{ts,tsx,js,html,scss,css}

📄 CodeRabbit inference engine (CLAUDE.md)

{apps,packages}/**/*.{ts,tsx,js,html,scss,css}: License headers are required on all source files
Always prepend 'Generated with Claude Code (https://claude.ai/code)' if Claude Code assisted with the code

Files:

  • apps/lfx-one/src/app/modules/my-activity/components/vote-submitted-dialog/vote-submitted-dialog.component.html
  • packages/shared/src/interfaces/components.interface.ts
  • apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.html
  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-dashboard/mailing-list-dashboard.component.ts
  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-manage/mailing-list-manage.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-submitted-dialog/vote-submitted-dialog.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/activity-top-bar/activity-top-bar.component.ts
  • apps/lfx-one/src/app/shared/components/committee-selector/committee-selector.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/surveys-table/surveys-table.component.ts
  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-view/mailing-list-view.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts
  • apps/lfx-one/src/app/modules/my-activity/my-activity-dashboard/my-activity-dashboard.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts
apps/lfx-one/**/*.{ts,tsx,html,scss,css}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Prettier configuration with Tailwind integration in lfx-one app

Files:

  • apps/lfx-one/src/app/modules/my-activity/components/vote-submitted-dialog/vote-submitted-dialog.component.html
  • apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.html
  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-dashboard/mailing-list-dashboard.component.ts
  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-manage/mailing-list-manage.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-submitted-dialog/vote-submitted-dialog.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/activity-top-bar/activity-top-bar.component.ts
  • apps/lfx-one/src/app/shared/components/committee-selector/committee-selector.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/surveys-table/surveys-table.component.ts
  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-view/mailing-list-view.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts
  • apps/lfx-one/src/app/modules/my-activity/my-activity-dashboard/my-activity-dashboard.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts
apps/lfx-one/src/**/*.{html,scss,css}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Tailwind CSS with PrimeUI plugin and LFX colors for styling

Files:

  • apps/lfx-one/src/app/modules/my-activity/components/vote-submitted-dialog/vote-submitted-dialog.component.html
  • apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.html
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use TypeScript interfaces instead of union types for better maintainability

Files:

  • packages/shared/src/interfaces/components.interface.ts
  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-dashboard/mailing-list-dashboard.component.ts
  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-manage/mailing-list-manage.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-submitted-dialog/vote-submitted-dialog.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/activity-top-bar/activity-top-bar.component.ts
  • apps/lfx-one/src/app/shared/components/committee-selector/committee-selector.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/surveys-table/surveys-table.component.ts
  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-view/mailing-list-view.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts
  • apps/lfx-one/src/app/modules/my-activity/my-activity-dashboard/my-activity-dashboard.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts
packages/shared/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • packages/shared/src/interfaces/components.interface.ts
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js}: Do not nest ternary expressions
Always use async/await for promises in TypeScript/JavaScript
Use camelCase for variable and function names

Files:

  • packages/shared/src/interfaces/components.interface.ts
  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-dashboard/mailing-list-dashboard.component.ts
  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-manage/mailing-list-manage.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-submitted-dialog/vote-submitted-dialog.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/activity-top-bar/activity-top-bar.component.ts
  • apps/lfx-one/src/app/shared/components/committee-selector/committee-selector.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/surveys-table/surveys-table.component.ts
  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-view/mailing-list-view.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts
  • apps/lfx-one/src/app/modules/my-activity/my-activity-dashboard/my-activity-dashboard.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts
apps/lfx-one/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/lfx-one/src/**/*.{ts,tsx}: Use zoneless change detection with signals in Angular 19
Always use direct imports for standalone components - no barrel exports
Use ESLint and Prettier for code quality, following Angular-specific ESLint rules

Files:

  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-dashboard/mailing-list-dashboard.component.ts
  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-manage/mailing-list-manage.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-submitted-dialog/vote-submitted-dialog.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/activity-top-bar/activity-top-bar.component.ts
  • apps/lfx-one/src/app/shared/components/committee-selector/committee-selector.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/surveys-table/surveys-table.component.ts
  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-view/mailing-list-view.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts
  • apps/lfx-one/src/app/modules/my-activity/my-activity-dashboard/my-activity-dashboard.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts
apps/lfx-one/src/**/*.component.ts

📄 CodeRabbit inference engine (CLAUDE.md)

apps/lfx-one/src/**/*.component.ts: All PrimeNG components must be wrapped in LFX components for UI library independence
Add data-testid attributes when creating new components for reliable test targeting

Files:

  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-dashboard/mailing-list-dashboard.component.ts
  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-manage/mailing-list-manage.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-submitted-dialog/vote-submitted-dialog.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/activity-top-bar/activity-top-bar.component.ts
  • apps/lfx-one/src/app/shared/components/committee-selector/committee-selector.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/surveys-table/surveys-table.component.ts
  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-view/mailing-list-view.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts
  • apps/lfx-one/src/app/modules/my-activity/my-activity-dashboard/my-activity-dashboard.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts
🧠 Learnings (1)
📚 Learning: 2025-12-10T23:20:29.281Z
Learnt from: asithade
Repo: linuxfoundation/lfx-v2-ui PR: 206
File: apps/lfx-one/src/app/modules/meetings/components/agenda-template-selector/agenda-template-selector.component.ts:11-15
Timestamp: 2025-12-10T23:20:29.281Z
Learning: For Angular 19+ projects, standalone defaults to true for components, directives, and pipes when not explicitly declared. Therefore, components with an imports array in the Component decorator do not require an explicit standalone: true declaration to be standalone. In reviews, verify that a component without an explicit standalone flag and with an imports array will behave as a standalone component according to Angular 19+ defaults. If you need to enforce explicit standalone behavior for clarity or consistency, you can still add standalone: true, but it is not required by default in Angular 19+. Ensure this guidance is considered when reviewing any component.ts files across the codebase.

Applied to files:

  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-dashboard/mailing-list-dashboard.component.ts
  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-manage/mailing-list-manage.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-submitted-dialog/vote-submitted-dialog.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/activity-top-bar/activity-top-bar.component.ts
  • apps/lfx-one/src/app/shared/components/committee-selector/committee-selector.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/surveys-table/surveys-table.component.ts
  • apps/lfx-one/src/app/modules/mailing-lists/mailing-list-view/mailing-list-view.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts
  • apps/lfx-one/src/app/modules/my-activity/my-activity-dashboard/my-activity-dashboard.component.ts
  • apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts
🧬 Code graph analysis (8)
apps/lfx-one/src/app/modules/mailing-lists/mailing-list-dashboard/mailing-list-dashboard.component.ts (1)
packages/shared/src/interfaces/committee.interface.ts (1)
  • CommitteeReference (11-18)
apps/lfx-one/src/app/modules/mailing-lists/mailing-list-manage/mailing-list-manage.component.ts (1)
packages/shared/src/interfaces/committee.interface.ts (1)
  • CommitteeReference (11-18)
apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.ts (2)
apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts (1)
  • Component (25-639)
apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts (1)
  • Component (37-1067)
apps/lfx-one/src/app/modules/my-activity/components/vote-submitted-dialog/vote-submitted-dialog.component.ts (1)
apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts (1)
  • Component (23-329)
apps/lfx-one/src/app/shared/components/committee-selector/committee-selector.component.ts (2)
packages/shared/src/interfaces/committee.interface.ts (1)
  • CommitteeReference (11-18)
packages/shared/src/interfaces/components.interface.ts (1)
  • CommitteeSelectorOption (574-581)
apps/lfx-one/src/app/modules/mailing-lists/mailing-list-view/mailing-list-view.component.ts (1)
packages/shared/src/interfaces/committee.interface.ts (1)
  • CommitteeReference (11-18)
apps/lfx-one/src/app/modules/my-activity/my-activity-dashboard/my-activity-dashboard.component.ts (4)
packages/shared/src/constants/my-activity.constants.ts (1)
  • MY_ACTIVITY_TAB_OPTIONS (24-27)
packages/shared/src/interfaces/my-activity.interface.ts (1)
  • MyActivityTab (8-8)
packages/shared/src/interfaces/poll.interface.ts (1)
  • UserVote (11-28)
packages/shared/src/interfaces/survey.interface.ts (1)
  • UserSurvey (11-26)
apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts (4)
packages/shared/src/interfaces/poll.interface.ts (3)
  • UserVote (11-28)
  • VoteDetails (93-114)
  • PollAnswer (66-79)
packages/shared/src/utils/poll.utils.ts (2)
  • CombinedVoteStatus (11-11)
  • getCombinedVoteStatus (22-32)
packages/shared/src/constants/my-activity.constants.ts (1)
  • MY_ACTIVITY_FILTER_LABELS (34-37)
packages/shared/src/constants/poll.constants.ts (3)
  • COMBINED_VOTE_STATUS_LABELS (53-57)
  • MOCK_POLL_DESCRIPTIONS (167-173)
  • MOCK_POLL_QUESTIONS (73-161)
🔇 Additional comments (29)
apps/lfx-one/src/app/modules/my-activity/components/surveys-table/surveys-table.component.ts (5)

48-66: LGTM! Clean signal-based reactive architecture.

The refactoring to use Angular signals (input.required, computed, toSignal) follows modern Angular 19+ patterns. The separation of form controls from filter signals with explicit onChange handlers provides clear control flow.


77-88: Well-structured signal-based search debouncing.

The RxJS pipeline correctly handles the form-to-signal conversion with appropriate debouncing and null-safety.


90-115: LGTM!

The computed signal correctly derives status options from the input surveys with appropriate counting and ordering logic.


117-143: LGTM!

Committee options computation follows the same clean pattern as status options, with appropriate fallback to uid when name is unavailable.


145-169: LGTM!

The filtering logic correctly chains multiple filter criteria with case-insensitive search and proper signal dependencies. The defensive null handling on line 149 is slightly redundant given the signal's initialValue, but doesn't cause harm.

apps/lfx-one/src/app/modules/my-activity/my-activity-dashboard/my-activity-dashboard.component.ts (3)

4-4: LGTM!

The Signal type import is correctly added to support the explicit typing of isVotesTab and isSurveysTab properties.


32-35: LGTM!

Clean inline initialization of tabForm with proper typing. Removing constructor-based initialization simplifies the component.


28-29: Section organization looks clean.

The section comments provide good visual organization for the component's members.

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

4-4: LGTM! ScrollShadowDirective integration follows established patterns.

The implementation is consistent with recent-progress.component.ts (using protected access modifier). The directive import and @ViewChild wiring are correctly set up.

Also applies to: 11-11, 19-19, 24-25

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

69-83: Shadow fade implementation is well-structured.

Good use of aria-hidden="true" and pointer-events-none for accessibility. The defensive scrollShadowDirective && check handles potential timing issues with @ViewChild initialization.

Minor note: The hardcoded color #f9fafb (gray-50) in the gradients could be extracted to a CSS variable if theme consistency becomes important, but this is fine for now.


31-66: Content structure is clean and follows guidelines.

  • Proper use of @for with track for efficient rendering
  • Conditional section rendering avoids empty containers
  • data-testid attributes follow the [section]-[component]-[element] naming convention
  • Uses flex + gap-* for spacing per coding guidelines
CLAUDE.md (1)

88-197: Well-structured documentation for Angular signal patterns.

The component organization pattern documentation is comprehensive and provides clear guidance on:

  • WritableSignals vs Model Signals usage
  • Private initializer functions for complex computed signals
  • Consistent component structure ordering
  • Interface centralization in the shared package

This aligns well with the Angular 19 patterns used throughout the codebase and will help maintain consistency.

packages/shared/src/interfaces/components.interface.ts (1)

582-603: Well-designed shared interfaces with proper generics.

The new RelativeDateInfo and TabOption<T> interfaces are:

  • Properly documented with JSDoc comments consistent with the file's style
  • Using TypeScript generics appropriately (TabOption<T = string>)
  • Centralized in the shared package as per coding guidelines

This enables type-safe usage across components like ActivityTopBarComponent.

apps/lfx-one/src/app/modules/my-activity/components/activity-top-bar/activity-top-bar.component.ts (2)

7-7: Good refactor to use shared TabOption interface.

Importing TabOption from @lfx-one/shared/interfaces centralizes the interface definition as per coding guidelines.


14-25: Clean component structure with proper organization.

The component follows good practices:

  • readonly modifiers on input signals
  • Generic typing with TabOption<MyActivityTab>[] for type safety
  • Clear section comments for code organization
apps/lfx-one/src/app/modules/mailing-lists/mailing-list-manage/mailing-list-manage.component.ts (2)

12-12: Type alignment with CommitteeReference.

Good refactor to use the authoritative CommitteeReference type instead of the deprecated MailingListCommittee alias, maintaining consistency across mailing-list components.


226-226: Consistent FormControl typing.

The committees FormControl is correctly typed as CommitteeReference[] to match the imported type.

apps/lfx-one/src/app/modules/my-activity/components/vote-submitted-dialog/vote-submitted-dialog.component.html (1)

1-25: Well-structured success dialog template.

The template follows coding guidelines:

  • Uses flex + flex-col + gap-* instead of space-y-*
  • Includes data-testid attribute for testing
  • Proper license header
  • Uses wrapped lfx-button component
apps/lfx-one/src/app/modules/my-activity/components/vote-submitted-dialog/vote-submitted-dialog.component.ts (1)

1-20: LGTM!

Clean, minimal dialog component implementation. License header is present, and the component correctly leverages Angular 19+ standalone defaults (no explicit standalone: true needed with the imports array). The injected DynamicDialogConfig provides access to the pollName data passed from showSuccessDialog() in the drawer component.

apps/lfx-one/src/app/modules/mailing-lists/mailing-list-view/mailing-list-view.component.ts (1)

21-21: LGTM!

Type migration from MailingListCommittee to CommitteeReference is consistent with the broader PR refactoring across mailing list components. The CommitteeReference interface provides the required uid and name properties used in the computed signal at line 103.

Also applies to: 72-72

apps/lfx-one/src/app/modules/mailing-lists/mailing-list-dashboard/mailing-list-dashboard.component.ts (1)

11-11: LGTM!

Consistent type migration to CommitteeReference. The internal Map<string, CommitteeReference> correctly stores committee data with the expected uid and name properties used in the filtering and options generation logic.

Also applies to: 177-177

apps/lfx-one/src/app/shared/components/committee-selector/committee-selector.component.ts (1)

8-8: LGTM!

Comprehensive type migration from MailingListCommittee to CommitteeReference. The bidirectional mapping between CommitteeReference (using uid) and CommitteeSelectorOption (using id) is correctly implemented in both mapFormValueToSelectedItems and updateFormControl. The allowed_voting_statuses population using VOTING_STATUSES in multi-select mode aligns with the CommitteeReference interface definition.

Also applies to: 169-170, 182-196, 220-224

apps/lfx-one/src/app/modules/my-activity/components/vote-details-drawer/vote-details-drawer.component.ts (2)

1-40: Well-structured vote details drawer component.

The component follows Angular 19+ patterns with signals, computed properties, and reactive forms. The separation of concerns is clear with distinct sections for injections, inputs/outputs, forms, computed signals, and helper methods. The form handling correctly supports both single-choice and multiple-choice questions with pre-population for existing votes.

Also applies to: 41-82


103-143: This review comment is based on incorrect assumptions about the system architecture.

The code does not make actual API calls. The my-activity-dashboard component uses mock data only (lines 61–149 in my-activity-dashboard.component.ts), with no HTTP service or API integration. The voteSubmitted event emission in vote-details-drawer triggers the success dialog immediately (line 142), and the parent component (votes-table) only updates local state via the localModifications signal when the event is received. There is no parent API call to handle, no API error to catch, and no reason to defer the success dialog display.

Likely an incorrect or invalid review comment.

apps/lfx-one/src/app/modules/my-activity/components/votes-table/votes-table.component.ts (5)

1-34: LGTM - Imports and license header are properly structured.

The imports are well-organized and appropriate for the component's functionality. The license header follows the required format.


35-66: LGTM - Component structure follows Angular 19 patterns.

The component correctly uses Angular 19's standalone defaults (implicit with imports array), signal-based input with input.required<UserVote[]>(), and properly typed form controls. Based on learnings, the explicit standalone: true is not required in Angular 19+.


207-230: LGTM - Merged votes implementation is well-structured.

The optimistic update pattern with initMergedVotes is correctly implemented:

  • Early return optimization when no modifications exist
  • Proper spread operator usage to apply partial updates
  • Clean abstraction via getVotesSource() for future flexibility

232-263: Mock data only handles single-question poll results.

The generic_choice_votes is only populated for the first question (poll_questions[0].choices). For multi-question polls like poll-005 (which has 2 questions per the mock data), the results for additional questions won't be generated. This is acceptable for development but should be noted for future real implementation.

Additionally, the user's answer is hardcoded to always be the first choice (q.choices[0]), which may make testing different scenarios difficult.

Is this mock data scope sufficient for the current development phase, or should it be expanded to handle multi-question scenarios?


271-286: LGTM - Sorting implementation is correct and immutable.

The sorting logic properly:

  • Creates a new array to avoid mutating the original
  • Prioritizes actionable votes (open → submitted → closed)
  • Uses end_time as secondary sort for equal statuses

@asithade asithade merged commit a149d48 into main Jan 13, 2026
7 checks passed
@asithade asithade deleted the feat/LFXV2-975 branch January 13, 2026 19:20
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