Skip to content

feat(meetings): add ical subscribe + calendar view to meetings dashboard#697

Merged
dealako merged 7 commits into
mainfrom
feat/LFXV2-1771-ical-subscribe-meetings-dashboard
May 13, 2026
Merged

feat(meetings): add ical subscribe + calendar view to meetings dashboard#697
dealako merged 7 commits into
mainfrom
feat/LFXV2-1771-ical-subscribe-meetings-dashboard

Conversation

@manishdixitlfx
Copy link
Copy Markdown
Contributor

@manishdixitlfx manishdixitlfx commented May 12, 2026

Summary

Extends the iCal Subscribe modal shipped in LFXV2-1714 / PR #689 and adds a [List] [Calendar] view toggle to the global Meetings dashboard for the foundation and project lenses. Also delivers the BFF backend endpoint so the Subscribe links actually resolve end-to-end.

Related JIRAs: LFXV2-1771 (this), LFXV2-1770 (org lens, deferred), LFXV2-1772 (me lens, parked).

What changed

Frontend

  • Meetings dashboard (meetings-dashboard.component.ts/.html): new viewMode signal + isListView / isCalendarView computeds; new calendarEvents signal that projects the existing filteredMeetings() into FullCalendar EventInput[]; new onSubscribe() opens IcalSubscribeDialogComponent with the lens-appropriate feed URL and a Subscribe — <name> header. The [List] [Calendar] [Subscribe] toggle row sits between the filter bar and the meeting list; Create Meeting stays in the title row.
  • Committee meetings tab: parity fix — cancelled occurrences are now inert on the calendar (no click navigation, no pointer cursor).
  • Dialog data rename: IcalSubscribeDialogData.committeeNamename (the dialog is no longer committee-specific). The existing committee caller is updated.

Shared infrastructure

  • FullCalendar wrapper SCSS (fullcalendar.component.scss): the blanket bg-gray-100 / bg-blue-50 !important overrides on .fc-timegrid-event are now scoped to .fc-timegrid-event.meeting-event only. Without this, every event with an inline backgroundColor rendered with white text on a near-white background in Week view, making the calendar look empty. This also fixes the same latent Week-view bug on the committee tab.
  • FullCalendar wrapper TS: scrollTime fixed at '06:00:00' (was scrolling to the current hour, which overshoots past the morning when checking after lunch). This also affects all other calendars in the app.

Backend (BFF — Express in this repo)

  • controllers/project.controller.ts: new getProjectCalendar mirroring getCommitteeCalendar exactly — public access via M2M token, paginated upcoming + past meetings filtered by tags: project_uid:<id>, reuses meetingsToVEvents + buildVCalendar, returns text/calendar with Cache-Control: public, max-age=900.
  • routes/public-projects.route.ts (new): single public route GET /:id/calendar.ics.
  • ⚠️ server.ts (protected file): wires app.use('/public/api/projects', publicProjectsRouter). Required for the route to work; flagged for code-owner attention.

Shared types

  • packages/shared/src/interfaces/committee.interface.ts: IcalSubscribeDialogData.committeeNamename.

Out of scope (intentional)

  • Me lens — parked in LFXV2-1772. A personal subscription URL requires a token design (calendar clients cannot carry session cookies).
  • Org lens — parked in LFXV2-1770.
  • Foundation feed roll-up: currently includes only meetings directly tagged to the foundation (project_uid:<foundation_uid>). Sub-project meetings are not yet rolled up — would need a new upstream tag or a cross-project fan-out. Suitable for a follow-up.

Notes for reviewers

  • server.ts change is protected — only adds one import and one app.use(...) line; pattern matches the existing publicCommitteesRouter registration.
  • Two shared-component changes affect all calendars (Week view background fix + scrollTime). These are improvements; the committee tab benefits too.
  • No upstream Go service changes — the BFF generates the iCal feed itself from regular meeting queries, mirroring how /public/api/committees/:id/calendar.ics already works.

🤖 Generated with Claude Code

LFXV2-1771: Extend the iCal Subscribe modal (LFXV2-1714 / PR #689) and
a List/Calendar view toggle to the global Meetings dashboard for the
foundation and project lenses.

Frontend (apps/lfx-one):
- meetings-dashboard.component.ts: add viewMode signal + isListView /
  isCalendarView computeds; add calendarEvents signal projecting
  filteredMeetings into FullCalendar EventInput[]; add onSubscribe()
  that opens IcalSubscribeDialogComponent with the correct lens-aware
  feed URL; add onCalendarEventClick() (cancelled occurrences are inert).
- meetings-dashboard.component.html: introduce the
  [List] [Calendar] [Subscribe] toggle row between the filter bar and
  the meeting list; render calendar legend + <lfx-fullcalendar> when
  isCalendarView() is active; Create Meeting stays at the top of the
  title row.
- committee-meetings.component.ts: parity fix — cancelled occurrences
  no longer accept clicks and render with cursor-default.
- ical-subscribe-dialog.component.ts + committees caller: rename
  dialog data key committeeName → name (dialog is no longer
  committee-specific).

Shared infrastructure (apps/lfx-one/src/app/shared/components/fullcalendar):
- fullcalendar.component.scss: scope the timegrid bg-gray-100 /
  bg-blue-50 forced backgrounds to .meeting-event-classed entries
  only. Previously the blanket !important override made any
  inline-coloured event invisible in Week view (white text on
  near-white background). This also fixes the latent Week-view
  visibility bug on the committee tab.
- fullcalendar.component.ts: scrollTime fixed at 06:00 instead of
  the current hour, so Week view opens to a sensible starting hour.

Backend (apps/lfx-one/src/server):
- controllers/project.controller.ts: add getProjectCalendar that
  mirrors getCommitteeCalendar exactly — public access via M2M
  token, paginated upcoming + past meetings filtered by tag
  project_uid:<id>, helpers meetingsToVEvents + buildVCalendar,
  text/calendar response with 15-minute Cache-Control.
- routes/public-projects.route.ts (NEW): single public route
  GET /:id/calendar.ics → projectController.getProjectCalendar.
- server.ts (protected): wire app.use('/public/api/projects',
  publicProjectsRouter).

Shared types (packages/shared/src/interfaces):
- IcalSubscribeDialogData.committeeName → name to reflect the
  broader use case.

Scope and follow-ups:
- The "Me" lens is intentionally not implemented (parked in
  LFXV2-1772). A personal subscription URL needs a token design
  because calendar clients cannot carry session cookies.
- The "Org" lens is parked in LFXV2-1770.
- Foundation feed currently includes only meetings directly tagged
  to the foundation (project_uid:<foundation_uid>). Sub-project
  meetings are not yet rolled up; that requires either a new
  upstream tag or a cross-project fan-out and can be a follow-up.

Signed-off-by: Manish Dixit <mdixit@linuxfoundation.org>
Copilot AI review requested due to automatic review settings May 12, 2026 22:02
@manishdixitlfx manishdixitlfx requested a review from a team as a code owner May 12, 2026 22:02
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 12, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 809d8c74-d048-45c5-8a68-8c024fbcb3fd

📥 Commits

Reviewing files that changed from the base of the PR and between 7067f94 and 27fb478.

📒 Files selected for processing (2)
  • apps/lfx-one/src/app/modules/meetings/meetings-dashboard/meetings-dashboard.component.html
  • apps/lfx-one/src/app/modules/meetings/meetings-dashboard/meetings-dashboard.component.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/lfx-one/src/app/modules/meetings/meetings-dashboard/meetings-dashboard.component.html
  • apps/lfx-one/src/app/modules/meetings/meetings-dashboard/meetings-dashboard.component.ts

Walkthrough

Adds a calendar view and iCal subscription flow: shared interface rename to name, iCal dialog updated, committee calendar cancelled-event handling, Meetings dashboard calendar toggle and event mapping, FullCalendar styling/config tweaks, and server endpoints/routes for project/committee ICS feeds.

Changes

Calendar iCal Subscription and Meeting Calendar View

Layer / File(s) Summary
Shared Interface Contract
packages/shared/src/interfaces/committee.interface.ts
IcalSubscribeDialogData interface now uses name: string instead of committeeName: string.
iCal Dialog Component Refactoring
apps/lfx-one/src/app/modules/committees/components/ical-subscribe-dialog/ical-subscribe-dialog.component.ts
Dialog derives name from dialogConfig.data?.name (defaults to "Calendar") and uses it in Outlook Live/365 URL templates.
Committee Meetings iCal Integration
apps/lfx-one/src/app/modules/committees/components/committee-meetings/committee-meetings.component.ts, apps/lfx-one/src/app/modules/committees/components/committee-meetings/committee-meetings.component.html
Passes data.name to iCal dialog, treats cancelled occurrences as inert (no navigation), marks cancelled occurrences with cursor-default and meeting-event classes, and updates legend color.
Meetings Dashboard UI & State
apps/lfx-one/src/app/modules/meetings/meetings-dashboard/meetings-dashboard.component.ts, apps/lfx-one/src/app/modules/meetings/meetings-dashboard/meetings-dashboard.component.html
Adds viewMode signal and computed selectors, updates component imports/providers, inserts List/Calendar toggle and conditional Subscribe button, and places the calendar legend above the calendar area.
Meetings Dashboard Calendar Logic
apps/lfx-one/src/app/modules/meetings/meetings-dashboard/meetings-dashboard.component.ts
Adds calendarEvents/calendarLoading, maps filtered meetings into FullCalendar EventInput[], expands recurring meetings to occurrences with stable IDs, marks cancelled occurrences with extendedProps.cancelled, and implements onCalendarEventClick and onSubscribe.
FullCalendar Styling & Config
apps/lfx-one/src/app/shared/components/fullcalendar/fullcalendar.component.scss, apps/lfx-one/src/app/shared/components/fullcalendar/fullcalendar.component.ts
Restricts timegrid dimming to .fc-timegrid-event.meeting-event, adds .fc-event.cursor-default inert styling, and sets scrollTime to '06:00:00'.
Backend Committee Calendar Filtering
apps/lfx-one/src/server/controllers/committee.controller.ts, apps/lfx-one/src/server/helpers/ics.helper.ts
Filters committee calendar output to MeetingVisibility.PUBLIC and non-restricted entries, uses explicit PRODID for buildVCalendar, and reports filtered_out.
Backend Project Calendar Endpoint
apps/lfx-one/src/server/controllers/project.controller.ts
Implements getProjectCalendar with id validation, optional M2M token generation, parallel paginated fetch of upcoming+past meetings, public/non-restricted filtering, .ics generation, response headers, and error forwarding.
Backend Route & Server Integration
apps/lfx-one/src/server/routes/public-projects.route.ts, apps/lfx-one/src/server/server.ts
Creates new public Express router for GET /:id/calendar.ics and mounts it at /public/api/projects.
Tooling
.husky/commit-msg
Quotes the $1 argument when invoking commitlint in the commit-msg hook.
sequenceDiagram
  participant User
  participant MeetingsDashboard
  participant IcalSubscribeDialog
  participant PublicAPI
  participant ProjectController
  participant MeetingService
  User->>MeetingsDashboard: click Subscribe / click calendar event
  MeetingsDashboard->>IcalSubscribeDialog: open({ feedUrl, name })
  MeetingsDashboard->>PublicAPI: GET /public/api/projects/:id/calendar.ics
  PublicAPI->>ProjectController: handle getProjectCalendar(req)
  ProjectController->>MeetingService: fetch upcoming & past meetings (paginated)
  ProjectController->>ProjectController: filter to PUBLIC && !restricted
  ProjectController->>PublicAPI: return .ics payload
  MeetingsDashboard->>User: show calendar / navigate to meeting (unless cancelled)
Loading

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding ical subscribe functionality and a calendar view toggle to the meetings dashboard.
Description check ✅ Passed The description provides comprehensive details about the changeset, covering frontend enhancements, shared component updates, backend implementation, and intentional scope exclusions.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

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

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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

Copy link
Copy Markdown
Contributor

@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: 3

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

Inline comments:
In
`@apps/lfx-one/src/app/modules/meetings/meetings-dashboard/meetings-dashboard.component.html`:
- Around line 225-245: The calendar branch in meetings-dashboard.component.html
(inside the isCalendarView() block that renders <lfx-fullcalendar> and the
loading spinner) doesn't trigger pagination, so calendar view only shows page 1
while list view calls loadMore(); update the calendar branch to invoke the same
pagination fallback used by list view: when (timeFilter() === 'upcoming' &&
meetingsLoading()) || (timeFilter() === 'past' && pastMeetingsLoading()) show
the spinner AND, when calendarEvents() is rendered, ensure you call or wire the
existing loadMore() mechanism (or expose the component method that triggers
loading additional pages) so additional pages are fetched as the calendar needs
them (use the same loadMore() helper used elsewhere to keep behavior consistent
with list mode).

In
`@apps/lfx-one/src/app/modules/meetings/meetings-dashboard/meetings-dashboard.component.ts`:
- Around line 252-253: The feed URL should encode the project ID to avoid
breaking links when it contains reserved characters; update the template that
builds feedUrl (the const feedUrl using projectCtx.uid in
meetings-dashboard.component.ts) to use an encoded ID (e.g., replace
projectCtx.uid with encodeURIComponent(projectCtx.uid)) so the path segment is
safely escaped while leaving the rest of the URL and the name assignment
unchanged.

In `@apps/lfx-one/src/server/controllers/project.controller.ts`:
- Around line 881-888: getProjectCalendar currently aggregates meetings via
fetchAllMeetingPages / meetingService.getMeetings but doesn't filter out
non-public items; after collecting upcoming and past (the variables upcoming and
past) combine them and filter to only include meetings where meeting.visibility
=== MeetingVisibility.PUBLIC and not meeting.restricted before rendering the
ICS. Apply the same change in getCommitteeCalendar: after fetching its
upcoming/past pages, replace the raw concatenation with a filtered list (use
MeetingVisibility.PUBLIC and the restricted flag) so private or restricted
meetings are excluded from the public calendar.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bdf6de84-cdfe-4879-9745-fca34e991903

📥 Commits

Reviewing files that changed from the base of the PR and between 4de59ce and 6f538df.

📒 Files selected for processing (10)
  • apps/lfx-one/src/app/modules/committees/components/committee-meetings/committee-meetings.component.ts
  • apps/lfx-one/src/app/modules/committees/components/ical-subscribe-dialog/ical-subscribe-dialog.component.ts
  • apps/lfx-one/src/app/modules/meetings/meetings-dashboard/meetings-dashboard.component.html
  • apps/lfx-one/src/app/modules/meetings/meetings-dashboard/meetings-dashboard.component.ts
  • apps/lfx-one/src/app/shared/components/fullcalendar/fullcalendar.component.scss
  • apps/lfx-one/src/app/shared/components/fullcalendar/fullcalendar.component.ts
  • apps/lfx-one/src/server/controllers/project.controller.ts
  • apps/lfx-one/src/server/routes/public-projects.route.ts
  • apps/lfx-one/src/server/server.ts
  • packages/shared/src/interfaces/committee.interface.ts

Comment thread apps/lfx-one/src/server/controllers/project.controller.ts
Copy link
Copy Markdown
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

Adds end-to-end iCal subscription for project/foundation meetings and introduces a calendar view on the Meetings dashboard, reusing the existing iCal Subscribe dialog and FullCalendar wrapper.

Changes:

  • Frontend: Adds a List/Calendar toggle and Subscribe action to the Meetings dashboard; maps filtered meetings into FullCalendar events and handles event-click navigation (with cancelled occurrences intended to be inert).
  • Backend: Adds a public /public/api/projects/:id/calendar.ics ICS feed endpoint (route + controller method) mirroring the existing committee calendar feed pattern.
  • Shared/UI: Generalizes the iCal Subscribe dialog data (committeeNamename) and adjusts FullCalendar wrapper styling/behavior (scoped timegrid overrides + fixed scrollTime).

Reviewed changes

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

Show a summary per file
File Description
packages/shared/src/interfaces/committee.interface.ts Renames iCal subscribe dialog data field to be non-committee-specific (name).
apps/lfx-one/src/server/server.ts Registers new public projects router under /public/api/projects.
apps/lfx-one/src/server/routes/public-projects.route.ts Adds public route GET /:id/calendar.ics for project/foundation calendar feeds.
apps/lfx-one/src/server/controllers/project.controller.ts Implements getProjectCalendar() ICS feed generation via MeetingService + ICS helpers.
apps/lfx-one/src/app/shared/components/fullcalendar/fullcalendar.component.ts Sets week view scrollTime to a fixed 06:00:00.
apps/lfx-one/src/app/shared/components/fullcalendar/fullcalendar.component.scss Scopes timegrid event background overrides to .meeting-event only.
apps/lfx-one/src/app/modules/meetings/meetings-dashboard/meetings-dashboard.component.ts Adds viewMode, calendar event projection, calendar click handling, and Subscribe modal wiring.
apps/lfx-one/src/app/modules/meetings/meetings-dashboard/meetings-dashboard.component.html Adds List/Calendar toggle UI, Subscribe button (foundation/project), and calendar view rendering + legend.
apps/lfx-one/src/app/modules/committees/components/ical-subscribe-dialog/ical-subscribe-dialog.component.ts Updates dialog to use generalized name for Outlook deep-links.
apps/lfx-one/src/app/modules/committees/components/committee-meetings/committee-meetings.component.ts Makes cancelled calendar occurrences inert (click short-circuit + attempts cursor change).

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

Comment thread apps/lfx-one/src/server/controllers/project.controller.ts Outdated
Address review comments from @coderabbitai and @copilot-pull-request-reviewer:

- project.controller.ts, committee.controller.ts: filter PRIVATE / restricted
  meetings out of the public iCal feeds. The public endpoints previously
  returned ALL meetings matching the project_uid / committee_uid tag — anyone
  with the UID could subscribe and see private meeting titles + times.
  Now mirrors PublicMeetingController.getMeeting's visibility guard:
  `visibility === MeetingVisibility.PUBLIC && !restricted`
  (per @coderabbitai — security blocker)
- meetings-dashboard.component.ts: switch the calendar event source from the
  list-view's paginated `filteredMeetings()` to the dashboard's non-paginated
  raw signals (rawUserMeetings + rawUserPastMeetings for the Me lens;
  rawFpUpcomingMeetings + rawFpPastMeetings for foundation/project). The
  calendar no longer silently misses meetings beyond the first scroll page
  (per @coderabbitai — major)
- meetings-dashboard.component.ts: wrap projectCtx.uid with encodeURIComponent
  in the feed URL so reserved characters in IDs don't break links
  (per @coderabbitai — minor)
- project.controller.ts: fix JSDoc route comment — was `GET /projects/:id/...`,
  now matches the actual public path `GET /public/api/projects/:id/calendar.ics`
  (per @copilot-pull-request-reviewer)
- fullcalendar.component.scss: add explicit `.fc-event.cursor-default {
  cursor: default !important; }` override so cancelled occurrences (and the
  vote/survey markers on the committee tab) actually drop the pointer
  cursor — the wrapper's `.fc .fc-event { cursor: pointer }` rule has
  higher specificity than Tailwind's `.cursor-default` utility, so
  classNames: ['cursor-default'] alone wouldn't win. Also disable the
  hover-lift on inert events (per @copilot-pull-request-reviewer)
- meetings-dashboard.component.html, committee-meetings.component.html:
  legend dot for "Meeting (default)" changed from bg-blue-600 → bg-blue-500
  so it matches MEETING_TYPE_COLORS.default (#3b82f6 — Tailwind blue-500)
  (per @copilot-pull-request-reviewer)

Resolves 7 review threads.

Signed-off-by: Manish Dixit <mdixit@linuxfoundation.org>
@manishdixitlfx
Copy link
Copy Markdown
Contributor Author

Review Feedback Addressed

Commit: bfd0993

Security fix (data leakage)

  • project.controller.ts, committee.controller.ts: filter visibility === PUBLIC && !restricted before generating the iCal feed. The public endpoints previously exposed PRIVATE meeting titles + times to anyone holding the project/committee UID. Mirrors PublicMeetingController.getMeeting's guard. (per @coderabbitai)

Calendar correctness

  • meetings-dashboard.component.ts: switched calendar event source from paginated filteredMeetings() to the non-paginated raw signals (rawUserMeetings/rawUserPastMeetings for Me lens; rawFpUpcomingMeetings/rawFpPastMeetings for foundation/project). Calendar now reflects the full set regardless of how far the user scrolled the list. No extra API calls — these signals are already fetched for the stat cards. (per @coderabbitai)
  • meetings-dashboard.component.ts: encodeURIComponent(projectCtx.uid) in the feed URL. (per @coderabbitai)

Polish

  • project.controller.ts: JSDoc route corrected to GET /public/api/projects/:id/calendar.ics. (per @copilot)
  • fullcalendar.component.scss: explicit .fc-event.cursor-default { cursor: default !important; } + disable hover-lift on inert events, so cancelled occurrences (and vote/survey markers on the committee tab) actually drop the pointer cursor. (per @copilot)
  • meetings-dashboard.component.html, committee-meetings.component.html: legend dot for "Meeting (default)" changed from bg-blue-600bg-blue-500 to match MEETING_TYPE_COLORS.default (#3b82f6). (per @copilot)

Threads Resolved

7 of 7 unresolved threads addressed in this iteration.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 12, 2026

@manishdixitlfx: Thank you for the thorough summary — all the addressed items look well-considered. A few highlights worth calling out:

  • Security fix — adding the visibility === PUBLIC && !restricted guard to both project.controller.ts and committee.controller.ts is exactly the right approach and correctly mirrors the existing guard in PublicMeetingController.getMeeting. This closes the data-leakage risk on both public calendar endpoints.
  • Calendar source switch — using the non-paginated raw signals (rawFpUpcomingMeetings/rawFpPastMeetings, etc.) ensures the calendar view is always complete, regardless of list scroll position, without incurring additional API calls. Clean solution.
  • encodeURIComponent(projectCtx.uid) — important defensive fix for any UIDs that contain characters unsafe in a URL.
  • Cursor & hover polish — the explicit .fc-event.cursor-default { cursor: default !important; } rule combined with disabling the hover-lift is a nice UX detail for cancelled occurrences.
  • Legend dot color (bg-blue-600bg-blue-500) — good catch keeping it pixel-perfect with MEETING_TYPE_COLORS.default.

I'll kick off a fresh review against the updated commit.

(◕‿◕✿) 🐇

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Comment thread apps/lfx-one/src/server/controllers/committee.controller.ts Outdated
Comment thread apps/lfx-one/src/server/controllers/project.controller.ts Outdated
Comment thread apps/lfx-one/src/server/controllers/project.controller.ts Outdated
Comment thread apps/lfx-one/src/server/controllers/project.controller.ts Outdated
Comment thread apps/lfx-one/src/app/shared/components/fullcalendar/fullcalendar.component.scss Outdated
Comment thread apps/lfx-one/src/app/shared/components/fullcalendar/fullcalendar.component.scss Outdated
Comment thread apps/lfx-one/src/app/shared/components/fullcalendar/fullcalendar.component.ts Outdated
Comment thread apps/lfx-one/src/server/controllers/project.controller.ts Outdated
Comment thread apps/lfx-one/src/server/controllers/project.controller.ts Outdated
Comment thread apps/lfx-one/src/server/controllers/project.controller.ts Outdated
Comment-style fixes per @MRashad26 — collapse multi-line comments/JSDoc
to one line, delete WHAT-style section headers, extract inline computed()
to a private init function per the repo's one-line-max rule.

- meetings-dashboard.component.html: drop view-toggle section-header comment
- meetings-dashboard.component.ts: drop viewMode 3-line block, collapse
  calendarEvents 8-line block (and extract inline computed() to
  initCalendarEvents() matching the existing init pattern), collapse
  onSubscribe JSDoc, collapse meetingToEvents JSDoc, collapse
  cursor-default comment
- committee-meetings.component.ts: collapse cursor-default comment
- fullcalendar.component.scss: collapse the two 5-line dimming/cursor
  override comments
- fullcalendar.component.ts: collapse scrollTime 2-line comment
- committee.controller.ts: collapse 3-line PRIVATE-filter comment
- project.controller.ts: collapse meetingService injection comment,
  delete Calendar ICS section-header, collapse getProjectCalendar JSDoc,
  collapse M2M-token / pagination / visibility-filter comments

Also quote "\$1" in .husky/commit-msg so the hook works on paths
that contain spaces.

Resolves 17 review threads.

Signed-off-by: Manish Dixit <mdixit@linuxfoundation.org>
Copilot AI review requested due to automatic review settings May 13, 2026 14:22
@manishdixitlfx
Copy link
Copy Markdown
Contributor Author

Review Feedback Addressed (Round 2)

Commit: 11f9afef

All 17 unresolved threads from @MRashad26 were enforcing the same one-line-max comment rule (per CLAUDE.md / .claude/rules/development-rules.md). Every fix is mechanical and matches the reviewer's suggested wording (with minor tightening where it kept the WHY clearer).

Comment-style fixes

  • meetings-dashboard.component.html:189 — deleted view-toggle section-header HTML comment
  • meetings-dashboard.component.ts:68-70 — deleted 3-line viewMode block (signals are self-documenting)
  • meetings-dashboard.component.ts:210-217 — collapsed the 8-line calendarEvents block to one line and extracted the inline computed() into private initCalendarEvents() so it matches the init* pattern used elsewhere (initIsFiltered, initializeMeetingTypeOptions, etc.)
  • meetings-dashboard.component.ts:242-250 — collapsed onSubscribe JSDoc; JIRA refs (LFXV2-1772 / LFXV2-1770) stay in the PR description
  • meetings-dashboard.component.ts:790-794 — removed meetingToEvents JSDoc; method name + types document the WHAT
  • meetings-dashboard.component.ts:cancelled-cursor — also collapsed the identical cursor-default two-line comment in meetingToEvents to match the one in committee-meetings.component.ts
  • committee-meetings.component.ts:275-276 — collapsed cursor-default comment
  • fullcalendar.component.scss:24-28 — collapsed the dimming-scope comment
  • fullcalendar.component.scss:94-98 — collapsed the !important cursor comment
  • fullcalendar.component.ts:58-59 — collapsed scrollTime comment
  • committee.controller.ts:1047-1049 — collapsed PRIVATE/restricted filter comment
  • project.controller.ts:30-32 — collapsed MeetingService injection comment
  • project.controller.ts:854 — deleted // ── Calendar ICS Endpoint ── section-header divider
  • project.controller.ts:856-863 — collapsed getProjectCalendar JSDoc
  • project.controller.ts:875-877 — collapsed M2M-token comment
  • project.controller.ts:884-885 — collapsed pagination comment
  • project.controller.ts:891-893 — collapsed visibility-filter comment

Bonus: hook quoting fix

  • .husky/commit-msg — quoted "$1" so the commit-msg hook runs correctly on repo paths that contain spaces (encountered while addressing this round of feedback).

Threads Resolved

17 of 17 unresolved threads resolved in this iteration. No questions, no discussion items, no deferred work.

Validation

  • yarn format — clean
  • yarn lint — clean
  • yarn check-types — clean
  • yarn build — clean (production build, 30.7s)

Copy link
Copy Markdown
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

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

Comment thread apps/lfx-one/src/server/controllers/project.controller.ts Outdated
@asithade asithade changed the title feat(meetings): add iCal Subscribe + Calendar view to Meetings dashboard (LFXV2-1771) feat(meetings): add ical subscribe + calendar view to meetings dashboard May 13, 2026
Copy link
Copy Markdown
Contributor

@asithade asithade left a comment

Choose a reason for hiding this comment

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

LGTM — approving.

What I verified

  • Prior review threads all resolved in code (verified by reading the PR-branch files, not relying on "resolved" status):
    • coderabbit's security blocker: getProjectCalendar (project.controller.ts:891) and getCommitteeCalendar (committee.controller.ts:1048) both filter m.visibility === MeetingVisibility.PUBLIC && !m.restricted, mirroring PublicMeetingController's visibility guard. The filtered_out count is also logged for observability.
    • Calendar view pagination gap: initCalendarEvents() now reads non-paginated raw signals (rawUserMeetings / rawFpUpcomingMeetings etc.), so the calendar reflects the full set regardless of scroll position.
    • URL hygiene: encodeURIComponent(projectCtx.uid) in the feed URL.
    • JSDoc route path corrected.
    • cursor-default specificity fix: explicit .fc-event.cursor-default { cursor: default !important; } override added in fullcalendar.component.scss.
    • Legend color matches MEETING_TYPE_COLORS.default (bg-blue-500).
    • All 17 MRashad26 comment-style fixes applied (one-line max, inline computed() extracted to initCalendarEvents() private init).
  • Upstream API contract: validated against lfx-v2-query-service and lfx-v2-meeting-service OpenAPI specs — tags, page_size, page_token, filters, visibility, restricted all match.
  • M2M usage: appropriate — /public/api/projects/:id/calendar.ics is a genuinely public route with no user session, mirroring getCommitteeCalendar's established pattern.

Protected files touched (acknowledged)

  • apps/lfx-one/src/server/server.ts — one import + one app.use('/public/api/projects', publicProjectsRouter) mount that cleanly mirrors the publicCommitteesRouter line directly above. Safe.
  • .husky/commit-msg$1"$1", the correct shellcheck SC2086 hardening for commit-message paths containing whitespace. Safe.

Optional nit (non-blocking)

  • '#ffffff' hard-coded for FullCalendar EventInput.textColor in meetings-dashboard.component.ts:791,807. Consistent with the same literal already used in committee-meetings.component.ts. If you ever want to centralize, an EVENT_TEXT_COLOR constant alongside MEETING_TYPE_COLORS in @lfx-one/shared/constants would do it — but leaving as-is is fine.

Nice work threading the security fix and the cursor-specificity fix through both controllers / both calendar surfaces.

Copy link
Copy Markdown
Contributor

@asithade asithade left a comment

Choose a reason for hiding this comment

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

LGTM — approving.

What I verified

  • Prior review threads all resolved in code (verified by reading the PR-branch files, not relying on "resolved" status):
    • coderabbit's security blocker: getProjectCalendar (project.controller.ts:891) and getCommitteeCalendar (committee.controller.ts:1048) both filter m.visibility === MeetingVisibility.PUBLIC && !m.restricted, mirroring PublicMeetingController's visibility guard. The filtered_out count is also logged for observability.
    • Calendar view pagination gap: initCalendarEvents() now reads non-paginated raw signals (rawUserMeetings / rawFpUpcomingMeetings etc.), so the calendar reflects the full set regardless of scroll position.
    • URL hygiene: encodeURIComponent(projectCtx.uid) in the feed URL.
    • JSDoc route path corrected.
    • cursor-default specificity fix: explicit .fc-event.cursor-default { cursor: default !important; } override added in fullcalendar.component.scss.
    • Legend color matches MEETING_TYPE_COLORS.default (bg-blue-500).
    • All 17 MRashad26 comment-style fixes applied (one-line max, inline computed() extracted to initCalendarEvents() private init).
  • Upstream API contract: validated against lfx-v2-query-service and lfx-v2-meeting-service OpenAPI specs — tags, page_size, page_token, filters, visibility, restricted all match.
  • M2M usage: appropriate — /public/api/projects/:id/calendar.ics is a genuinely public route with no user session, mirroring getCommitteeCalendar's established pattern.

Protected files touched (acknowledged)

  • apps/lfx-one/src/server/server.ts — one import + one app.use('/public/api/projects', publicProjectsRouter) mount that cleanly mirrors the publicCommitteesRouter line directly above. Safe.
  • .husky/commit-msg$1"$1", the correct shellcheck SC2086 hardening for commit-message paths containing whitespace. Safe.

Optional nit (non-blocking)

  • '#ffffff' hard-coded for FullCalendar EventInput.textColor in meetings-dashboard.component.ts:791,807. Consistent with the same literal already used in committee-meetings.component.ts. If you ever want to centralize, an EVENT_TEXT_COLOR constant alongside MEETING_TYPE_COLORS in @lfx-one/shared/constants would do it — but leaving as-is is fine.

Nice work threading the security fix and the cursor-specificity fix through both controllers / both calendar surfaces.

asithade
asithade previously approved these changes May 13, 2026
Copy link
Copy Markdown
Contributor

@asithade asithade left a comment

Choose a reason for hiding this comment

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

LGTM — approving.

What I verified

  • Prior review threads all resolved in code (verified by reading the PR-branch files, not relying on "resolved" status):
    • coderabbit's security blocker: getProjectCalendar (project.controller.ts:891) and getCommitteeCalendar (committee.controller.ts:1048) both filter m.visibility === MeetingVisibility.PUBLIC && !m.restricted, mirroring PublicMeetingController's visibility guard. The filtered_out count is also logged for observability.
    • Calendar view pagination gap: initCalendarEvents() now reads non-paginated raw signals (rawUserMeetings / rawFpUpcomingMeetings etc.), so the calendar reflects the full set regardless of scroll position.
    • URL hygiene: encodeURIComponent(projectCtx.uid) in the feed URL.
    • JSDoc route path corrected.
    • cursor-default specificity fix: explicit .fc-event.cursor-default { cursor: default !important; } override added in fullcalendar.component.scss.
    • Legend color matches MEETING_TYPE_COLORS.default (bg-blue-500).
    • All 17 MRashad26 comment-style fixes applied (one-line max, inline computed() extracted to initCalendarEvents() private init).
  • Upstream API contract: validated against lfx-v2-query-service and lfx-v2-meeting-service OpenAPI specs — tags, page_size, page_token, filters, visibility, restricted all match.
  • M2M usage: appropriate — /public/api/projects/:id/calendar.ics is a genuinely public route with no user session, mirroring getCommitteeCalendar's established pattern.

Protected files touched (acknowledged)

  • apps/lfx-one/src/server/server.ts — one import + one app.use('/public/api/projects', publicProjectsRouter) mount that cleanly mirrors the publicCommitteesRouter line directly above. Safe.
  • .husky/commit-msg$1"$1", the correct shellcheck SC2086 hardening for commit-message paths containing whitespace. Safe.

Optional nit (non-blocking)

  • '#ffffff' hard-coded for FullCalendar EventInput.textColor in meetings-dashboard.component.ts:791,807. Consistent with the same literal already used in committee-meetings.component.ts. If you ever want to centralize, an EVENT_TEXT_COLOR constant alongside MEETING_TYPE_COLORS in @lfx-one/shared/constants would do it — but leaving as-is is fine.

Nice work threading the security fix and the cursor-specificity fix through both controllers / both calendar surfaces.

manishdixitlfx and others added 2 commits May 13, 2026 10:28
Address review comments from @copilot-pull-request-reviewer:

- meetings-dashboard.component.ts: initCalendarEvents now uses
  filterMeLensMeetings on the Me lens so Calendar applies the same
  foundation/project/pendingRsvp filters as the List view; FP lens
  keeps filterBySearchAndType since those filters aren't exposed there.
- meetings-dashboard.component.ts / committee-meetings.component.ts:
  meetingToEvents now adds 'meeting-event' to classNames (alongside
  'cursor-default' for cancelled occurrences). The shared FullCalendar
  SCSS scopes dimming + future-event coloring to .meeting-event, so
  meeting events need the class for the styling to apply.
- meetings-dashboard.component.{ts,html}: new calendarLoading computed
  reflects the actual source loading state (fpUpcomingLoading/fpPastLoading
  on FP lens, meetingsLoading/pastMeetingsLoading on Me lens) and drives
  the calendar spinner. Previously the spinner used the paginated list
  loaders, so FP calendar could appear empty while the raw fetch was
  still in flight.
- ics.helper.ts: buildVCalendar accepts an optional prodId
  (default '-//LFX//Calendar//EN'); committee.controller passes
  '-//LFX//Committee Calendar//EN', project.controller passes
  '-//LFX//Project Calendar//EN' so each feed identifies itself
  accurately.

Resolves 5 review threads.

Signed-off-by: Manish Dixit <mdixit@linuxfoundation.org>
Copilot AI review requested due to automatic review settings May 13, 2026 17:31
@manishdixitlfx
Copy link
Copy Markdown
Contributor Author

Review Feedback Addressed (Round 3)

Commit: 7067f94a

Five new substantive findings from @copilot-pull-request-reviewer, all valid and addressed in a single commit.

Changes Made

  • meetings-dashboard.component.tsinitCalendarEvents now branches on lens: Me lens uses filterMeLensMeetings (search + type + foundation + project + pendingRsvp), matching the List view; FP lens stays on filterBySearchAndType because the foundation/project/pendingRsvp filters aren't surfaced there.
  • meetings-dashboard.component.ts + committee-meetings.component.tsmeetingToEvents now adds 'meeting-event' to classNames (with 'cursor-default' appended for cancelled occurrences). The shared FullCalendar SCSS scopes dimming + future-event coloring to .meeting-event, so without the class the styling was silently a no-op.
  • meetings-dashboard.component.{ts,html} — new calendarLoading computed reflects the actual source loading state (fpUpcomingLoading/fpPastLoading for FP, meetingsLoading/pastMeetingsLoading for Me). The calendar spinner uses it. Previously the spinner used the paginated list loaders, so an FP-lens calendar could appear empty while the raw FP fetch was still in flight.
  • ics.helper.tsbuildVCalendar(events, prodId = '-//LFX//Calendar//EN') now accepts an optional PRODID. committee.controller passes -//LFX//Committee Calendar//EN, project.controller passes -//LFX//Project Calendar//EN, so committee and project feeds identify themselves accurately.

Threads Resolved

5 of 5 unresolved threads addressed in this iteration. No questions, no discussion items, no deferred work.

Validation

  • yarn format — clean
  • yarn lint — clean
  • yarn check-types — clean
  • yarn build — clean (production build)
  • Rebased on latest feat/LFXV2-1771-ical-subscribe-meetings-dashboard (which now includes the merge from main).

Copy link
Copy Markdown
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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

dealako and others added 2 commits May 13, 2026 13:54
LFXV2-1771

Address review comment from @copilot-pull-request-reviewer on
meetings-dashboard.component.html:208 — the Calendar toggle was
rendered on every lens, but the Me lens skips the raw
project/foundation fetch that clears `meetingsLoading` /
`pastMeetingsLoading`. As a result, opening Calendar on the Me lens
showed the spinner indefinitely because `calendarLoading` ORs those
two flags and both stay `true` forever for that lens.

Match the Subscribe button's existing gating: introduce a
`canShowCalendarView` computed (`activeLens === 'foundation' || === 'project'`)
and use it to (a) hide the Calendar toggle button, (b) tighten
`isCalendarView` so the calendar view div doesn't render either when
viewMode is 'calendar' on the Me lens (which can happen if the user
toggles calendar on foundation then switches lens). `viewMode` itself
is left alone so the user's calendar selection is restored when they
switch back to a supported lens.

The Subscribe button's inline lens check is also collapsed to use
the new computed for consistency.

Resolves 1 review thread.

Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: Manish Dixit <mdixit@linuxfoundation.org>
@manishdixitlfx
Copy link
Copy Markdown
Contributor Author

Review feedback addressed (round 4)

Commit: 27fb478

Changes Made

  • meetings-dashboard.component.ts + meetings-dashboard.component.html — gated the Calendar toggle to foundation/project lenses via a new canShowCalendarView computed; also tightened isCalendarView to short-circuit on unsupported lenses so the calendar view div doesn't render even if viewMode === 'calendar' is carried over from a prior lens. Fixes the infinite-spinner case @copilot-pull-request-reviewer flagged (Me lens skips the raw fetch, so meetingsLoading/pastMeetingsLoading never settle → calendarLoading stays true forever). Subscribe button's inline lens check collapsed to the same computed for consistency. (per @copilot-pull-request-reviewer)

Threads Resolved

1 of 1 remaining unresolved thread addressed in this iteration.

Re-review

Re-requested from @asithade (their previous approval was dismissed by the main-merge, not by a new finding; the rest of the prior fixes still stand per their 17:09 UTC verification).

🤖 Generated with Claude Code

Copy link
Copy Markdown
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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

apps/lfx-one/src/app/shared/components/fullcalendar/fullcalendar.component.scss:32

  • This selector also matches cancelled meeting occurrences because they now include both meeting-event and cursor-default. In Week view a future cancelled occurrence will have its inline CANCELLED_COLOR gray background overridden by bg-blue-50, so it no longer matches the cancelled legend or visual state. Exclude cancelled/cursor-default meeting events from the future/past background override, or give cancelled events a dedicated class with its own override.
          .fc-timegrid-event.meeting-event {
            &:not(.fc-event-future) {
              @apply bg-gray-100 #{!important};
            }

            &.fc-event-future {
              @apply bg-blue-50 #{!important};

Comment on lines +793 to +794
: this.filterBySearchAndType([...this.rawFpUpcomingMeetings(), ...this.rawFpPastMeetings()], search, meetingType);
return filtered.flatMap((m) => this.meetingToEvents(m));
Copy link
Copy Markdown
Contributor

@dealako dealako left a comment

Choose a reason for hiding this comment

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

Enough punishment. Time to approve.

@dealako dealako merged commit 65e0d72 into main May 13, 2026
17 checks passed
@dealako dealako deleted the feat/LFXV2-1771-ical-subscribe-meetings-dashboard branch May 13, 2026 22:57
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.

5 participants