feat(dashboards): add event roster with goal bars to overview - #1171
feat(dashboards): add event roster with goal bars to overview#1171mrautela365 wants to merge 1 commit into
Conversation
Add an Events roster table below the summary tiles, sourced from ANALYTICS.PLATINUM_LFX_ONE.MARKETING_EVENT_REGISTRATIONS joined to sponsorship actuals from MARKETING_EVENT_SPONSORSHIPS_BY_TIER. Each row shows registration and sponsorship actual-vs-goal as a health-colored progress bar (green/amber/red), a goal of 0 renders no bar (matches PCC's 'no goal required' rule), plus an at-risk flag for events behind goal with a low comparison pace. Includes an upcoming/all toggle and name search. LFXV2-2768 Signed-off-by: Misha Rautela <mrautela@linuxfoundation.org>
PR SummaryMedium Risk Overview Each row shows date, name (link when URL exists), registration and sponsorship revenue actual vs goal as color-coded progress bars (no bar when goal is 0, aligned with PCC), CFP status, and an at-risk indicator when registration is under 50% of goal and Shared types ( Reviewed by Cursor Bugbot for commit 8997b71. Bugbot is set up for automated code reviews on this repo. Configure here. |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 8997b71. Configure here.
| } @else { | ||
| <div class="flex items-center justify-center rounded-lg border border-dashed border-gray-300 bg-gray-50 p-8" data-testid="event-roster-empty"> | ||
| <p class="text-sm text-gray-500">No events match your search.</p> | ||
| </div> |
There was a problem hiding this comment.
Empty state blames search incorrectly
Medium Severity
The empty state always shows “No events match your search.” whenever hasRows() is false, but that branch also runs when the roster API returns zero events, the “Upcoming” scope hides all rows, or the search box is empty. Users with no upcoming events or an empty foundation see a search-specific message that does not apply.
Reviewed by Cursor Bugbot for commit 8997b71. Configure here.
| <span class="truncate text-xs font-semibold text-gray-700">{{ row.eventName }}</span> | ||
| } | ||
| @if (row.atRisk) { | ||
| <i class="fa-solid fa-circle-exclamation text-xs text-red-500" aria-label="Behind registration goal" title="Behind registration goal"></i> |
There was a problem hiding this comment.
At-risk icon misstates trigger conditions
Low Severity
The at-risk icon’s aria-label and title say the event is only “Behind registration goal,” but atRisk is set only when registration progress is under 50% of goal and compScore === 'low'. Assistive tech and hover users are not told the low year-over-year pace requirement.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 8997b71. Configure here.
There was a problem hiding this comment.
Pull request overview
Adds an LF Events roster to the Marketing Impact overview, backed by Snowflake registration and sponsorship data.
Changes:
- Adds roster API contracts, query, controller, route, and client service.
- Adds searchable upcoming/all event roster UI with goal bars and risk indicators.
- Integrates the roster below Events Summary.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
packages/shared/src/interfaces/marketing-impact.interface.ts |
Adds roster view models. |
packages/shared/src/interfaces/dashboard-metric.interface.ts |
Adds roster API contracts. |
apps/lfx-one/src/server/services/project.service.ts |
Queries and maps roster data. |
apps/lfx-one/src/server/routes/analytics.route.ts |
Registers the roster endpoint. |
apps/lfx-one/src/server/controllers/analytics.controller.ts |
Validates and handles roster requests. |
apps/lfx-one/src/app/shared/services/analytics.service.ts |
Adds the roster HTTP client. |
apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/overview-tab/overview-tab.component.ts |
Imports the roster component. |
apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/overview-tab/overview-tab.component.html |
Places the roster in the overview. |
apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/event-roster-section/event-roster-section.component.ts |
Implements roster state and formatting. |
apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/event-roster-section/event-roster-section.component.html |
Implements roster controls and table UI. |
Comments suppressed due to low confidence (1)
apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/event-roster-section/event-roster-section.component.html:34
- The active scope is conveyed only visually. Expose the toggle state so screen-reader users can determine when “All events” is selected.
[ngClass]="includePast() ? 'bg-blue-500 text-white' : 'text-gray-500'"
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| cfpStatus: row.CFP_STATUS ?? '', | ||
| })); | ||
|
|
||
| return { projectId: '', events }; |
| if (includePast) { | ||
| params['includePast'] = 'true'; | ||
| } | ||
| return this.http.get<EventRosterResponse>('/api/analytics/event-roster', { params }).pipe(catchError(() => of({ projectId: '', events: [] }))); |
| <h3 class="text-base font-semibold text-gray-900">Events</h3> | ||
| <p class="text-xs text-gray-500">Registrations and sponsorship vs goal · click an event to open it</p> | ||
| </div> | ||
| <div class="flex items-center gap-3"> |
| <button | ||
| type="button" | ||
| class="rounded-full px-3 py-1 text-xs font-semibold" | ||
| [ngClass]="!includePast() ? 'bg-blue-500 text-white' : 'text-gray-500'" |
| <div | ||
| class="h-full rounded-full" | ||
| [ngClass]="{ | ||
| 'bg-emerald-500': row.registrations.tone === 'good', | ||
| 'bg-amber-500': row.registrations.tone === 'warn', | ||
| 'bg-red-400': row.registrations.tone === 'critical', | ||
| }" | ||
| [style.width.%]="row.registrations.percent"></div> |
| <div | ||
| class="h-full rounded-full" | ||
| [ngClass]="{ | ||
| 'bg-emerald-500': row.sponsorshipRevenue.tone === 'good', | ||
| 'bg-amber-500': row.sponsorshipRevenue.tone === 'warn', | ||
| 'bg-red-400': row.sponsorshipRevenue.tone === 'critical', | ||
| }" | ||
| [style.width.%]="row.sponsorshipRevenue.percent"></div> |
| } | ||
| </div> | ||
| } @else if (hasRows()) { | ||
| <div class="flex flex-col gap-0 overflow-x-auto" role="table" aria-label="Event roster" data-testid="event-roster-table"> |
| </div> | ||
| } @else { | ||
| <div class="flex items-center justify-center rounded-lg border border-dashed border-gray-300 bg-gray-50 p-8" data-testid="event-roster-empty"> | ||
| <p class="text-sm text-gray-500">No events match your search.</p> |


Summary
Adds the Events roster table below the summary tiles, from
MARKETING_EVENT_REGISTRATIONSjoined to sponsorship actuals fromMARKETING_EVENT_SPONSORSHIPS_BY_TIER. Each row shows registration and sponsorship actual-vs-goal as a health-colored bar (green/amber/red); a goal of 0 renders no bar (matches PCC's "no goal required"). Includes an at-risk flag, an upcoming/all toggle, and name search.PR 2/6 of the LF Events dashboard stack (LFXV2-2768). Base:
feat/LFXV2-2768-events-summary(PR #1170). Review/merge PR 1 first.Queries validated live. Typecheck + lint pass.
LFXV2-2768