Skip to content

feat: wire Collection Contributors/Popularity/Development tabs to real widgets IN-1191#2013

Open
gaspergrom wants to merge 5 commits into
mainfrom
feat/collections-widget
Open

feat: wire Collection Contributors/Popularity/Development tabs to real widgets IN-1191#2013
gaspergrom wants to merge 5 commits into
mainfrom
feat/collections-widget

Conversation

@gaspergrom

Copy link
Copy Markdown
Collaborator

Summary

Collection detail pages (/collection/details/[slug]/{contributors,popularity,development}) now render the same widget system as individual Project pages — leaderboards, dependency charts, retention, geo-distribution, package/download widgets, PR metrics, etc. — aggregated across every project in the collection, instead of scoped to one project.

  • Reuses <lfx-widget-area> and the existing widget config system rather than building separate UI. Each widget config gets an availableInCollection flag controlling visibility on collection pages.
  • Backend routes are unified under /api/widget/<domain>/<name>.get.ts, scoped via query string (?project= or ?collectionSlug=) through a shared getWidgetScope() helper.
  • Most Tinybird pipes are branched to accept collectionSlug alongside project (via segments_filtered_by_collection). Two widgets (Contributors Leaderboard, Contributor Dependency) get dedicated collection-scoped pipes for performance, since a naive branch on the single-project pipe's window-function math times out at collection scale (16s+ on a 242-project collection).
  • Second commit fixes several bugs found during an extensive QA pass: scope leaks where leaderboard drawers and a share-badge feature silently queried the wrong scope, a non-functional activity-type filter dropdown, benchmark cards leaking onto collection pages with bogus data, an SSR crash in the non-curated-collection redirect guard, and a responsive tab-bar rewrite matching the project page's dropdown-fallback pattern at tablet/mobile widths.

Dependencies

Requires the following crowd.dev Tinybird pipes to be deployed (already live in prod as of this PR):

  • collection_contributors_leaderboard.pipe
  • collection_contributor_dependency.pipe
  • segments_filtered_by_collection.pipe
  • activityTypes_by_project.pipe (collectionSlug branch)
  • ~10 other pipes branched to accept collectionSlug (active_contributors, organizations_leaderboard, package_metrics, etc.)

See crowd.dev PR #4348 for the pipe-side changes.

Test plan

  • pnpm tsc-check clean
  • pnpm lint clean (9 pre-existing unrelated warnings)
  • pnpm test — 131/131 passing
  • Live-verified against CNCF (242 projects) and several smaller collections (academy-software-foundation, universal-blue, ebpf-projects) across all 3 tabs, both curated and non-curated collections
  • Verified at desktop (1440px), tablet (768px), and mobile (375px) widths
  • Regression-checked against project pages (atlantis, k8s) — unaffected

…l widgets IN-1191

Replaces the 3 stub collection tab views (a flat table + 2 metric
cards) with the same <lfx-widget-area> component project pages use,
so Collection detail pages show the full sidebar of widgets
(leaderboards, active contributors/organizations, dependency,
retention, geo-distribution, package downloads, PR metrics, etc.)
aggregated across every project in the collection.

Backend routing:
- Unifies /api/project/[slug]/{contributors,popularity,development}/*
  into /api/widget/{contributors,popularity,development}/* (28 routes),
  reading scope (project OR collectionSlug) from the query string via
  a new getWidgetScope() helper instead of a route param, since scope
  can now be either a single project or a whole collection.
- contributor-leaderboard is the one exception: it branches server-side
  between the existing contributors_leaderboard pipe (project scope,
  unchanged) and the new, separate, performance-optimized
  collection_contributors_leaderboard pipe (collection scope) - see
  that pipe's crowd.dev commit for why a naive collectionSlug port of
  the single-project pipe wasn't viable (window-function aggregation
  timed out at collection scale).
- Every other widget's data-layer function and Tinybird query type now
  accepts collectionSlug alongside project, forwarding it straight
  through to the already-deployed collectionSlug-branched pipes.

Frontend shell (mostly already in place from earlier this session):
- useProjectStore exposes isCollectionScope and short-circuits
  repo/archived-repo-dependent computeds to safe values in collection
  mode, since a collection has no single project's repos/widgets array.
- widget-area.vue filters the shown widgets by each widget config's new
  availableInCollection flag (instead of a project's widgets array) and
  skips the project-only benchmarks query entirely in collection mode.
- Every leaf widget component conditionally builds projectSlug vs
  collectionSlug from the same isCollectionScope flag instead of always
  assuming route.params.slug is a project.

Verified live against prod Tinybird: project-scoped calls unaffected
(regression-checked against real project data), collection-scoped
calls return correct aggregated data for a real 242-project collection
across contributors, popularity, and development widgets.

Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
- Fix leaderboard drawers and share-badge feature silently querying the
  wrong scope (project instead of collectionSlug) on collection pages
- Fix activity-type/platform filter dropdown never populating on
  collection pages (query gated on a null project slug)
- Fix Contributor Dependency and Code Review Engagement widgets calling
  the project-only contributors_leaderboard pipe on collection pages
- Fix per-widget benchmark tip-cards leaking onto collection pages with
  bogus point=0 data
- Fix Only Linux Foundation projects toggle showing on non-Projects tabs
- Fix metrics-row chip skeleton/content DOM-reuse causing clipped text
- Fix redirect-to-Projects-tab guard for non-curated collections crashing
  during SSR, and harden it against a stale-cache race
- Replicate project-menu.vue's responsive dropdown-fallback pattern in
  collection-menu.vue instead of a cramped scrollable tab row
- Hide GitHub/Press/Social Mentions widgets on collection pages (mock
  data only, no real backend - project pages unaffected)

Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Copilot AI review requested due to automatic review settings July 17, 2026 09:37
@gaspergrom
gaspergrom requested a review from joanagmaia July 17, 2026 09:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds collection-scoped Contributors, Popularity, and Development widgets by reusing the project widget system and unified APIs.

Changes:

  • Adds project-or-collection request scoping through frontend, API, and Tinybird layers.
  • Enables supported widgets and collection-specific leaderboard pipes.
  • Adds responsive collection navigation, date filtering, and scope-aware UI behavior.

Reviewed changes

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

Show a summary per file
File Description
frontend/types/development/requests.types.ts Adds collection scope to development filters.
frontend/server/utils/common.ts Adds shared widget-scope parsing.
frontend/server/data/types.ts Extends data filters for collections.
frontend/server/data/tinybird/requests.types.ts Adds collection Tinybird query types.
frontend/server/data/tinybird/popularity/search-queries.ts Forwards collection scope.
frontend/server/data/tinybird/popularity/package-metrics.ts Forwards collection scope.
frontend/server/data/tinybird/development/code-review-engagement.ts Adds collection leaderboard handling.
frontend/server/data/tinybird/development/active-days.ts Forwards collection scope.
frontend/server/data/tinybird/contributors/organizations-leaderboard.ts Supports collection queries.
frontend/server/data/tinybird/contributors/contributors-dependency.ts Uses dedicated collection pipes.
frontend/server/data/tinybird/contributors/collection-contributors-leaderboard.ts Adds collection leaderboard adapter.
frontend/server/data/tinybird/contributors/active-organizations.ts Forwards collection scope.
frontend/server/data/tinybird/contributors/active-contributors.ts Forwards collection scope.
frontend/server/data/tinybird/activity-types.ts Supports collection activity types.
frontend/server/data/data-sources.ts Registers collection leaderboard source.
frontend/server/api/widget/popularity/stars.get.ts Unifies widget scope.
frontend/server/api/widget/popularity/social-mentions.get.ts Updates scope documentation.
frontend/server/api/widget/popularity/search-queries.get.ts Unifies widget scope.
frontend/server/api/widget/popularity/press-mentions.get.ts Updates scope documentation.
frontend/server/api/widget/popularity/packages.get.ts Unifies widget scope.
frontend/server/api/widget/popularity/package-downloads.get.ts Unifies widget scope.
frontend/server/api/widget/popularity/mailing-lists-messages.get.ts Unifies widget scope.
frontend/server/api/widget/popularity/github-mentions.get.ts Updates scope documentation.
frontend/server/api/widget/popularity/forks.get.ts Unifies widget scope.
frontend/server/api/widget/development/review-time-by-pr-size.get.ts Unifies widget scope.
frontend/server/api/widget/development/review-efficiency.get.ts Unifies widget scope.
frontend/server/api/widget/development/pull-requests.get.ts Unifies widget scope.
frontend/server/api/widget/development/patchsets-per-review.get.ts Unifies widget scope.
frontend/server/api/widget/development/merge-lead-time.get.ts Unifies widget scope.
frontend/server/api/widget/development/median-time-to-review.get.ts Unifies widget scope.
frontend/server/api/widget/development/median-time-to-close.get.ts Unifies widget scope.
frontend/server/api/widget/development/issues-resolution.get.ts Unifies widget scope.
frontend/server/api/widget/development/contribution-outside.get.ts Unifies widget scope.
frontend/server/api/widget/development/commit-activities.get.ts Unifies widget scope.
frontend/server/api/widget/development/code-review-engagement.get.ts Unifies widget scope.
frontend/server/api/widget/development/active-days.get.ts Unifies widget scope.
frontend/server/api/widget/contributors/retention.get.ts Unifies widget scope.
frontend/server/api/widget/contributors/organization-leaderboard.get.ts Unifies widget scope.
frontend/server/api/widget/contributors/organization-dependency.get.ts Unifies widget scope.
frontend/server/api/widget/contributors/geographical-distribution.get.ts Unifies widget scope.
frontend/server/api/widget/contributors/contributor-leaderboard.get.ts Selects scope-specific leaderboard source.
frontend/server/api/widget/contributors/contributor-dependency.get.ts Unifies widget scope.
frontend/server/api/widget/contributors/activity-types.get.ts Unifies widget scope.
frontend/server/api/widget/contributors/active-organizations.get.ts Unifies widget scope.
frontend/server/api/widget/contributors/active-contributors.get.ts Unifies widget scope.
frontend/app/pages/collection/details/[slug]/popularity.vue Simplifies collection view mounting.
frontend/app/pages/collection/details/[slug]/development.vue Simplifies collection view mounting.
frontend/app/pages/collection/details/[slug]/contributors.vue Simplifies collection view mounting.
frontend/app/pages/collection/details/[slug].vue Adds dates and aggregate-tab guards.
frontend/app/components/shared/modules/share/store/share.api.service.ts Makes badge requests scope-aware.
frontend/app/components/shared/modules/share/components/active-contributor-badge.vue Selects collection or project scope.
frontend/app/components/modules/widget/services/popularity.api.service.ts Uses unified popularity APIs.
frontend/app/components/modules/widget/services/development.api.service.ts Uses unified development APIs.
frontend/app/components/modules/widget/services/contributors.api.service.ts Uses unified contributor APIs.
frontend/app/components/modules/widget/services/benchmarks.api.service.ts Supports disabling benchmark queries.
frontend/app/components/modules/widget/config/widget.config.ts Adds collection availability metadata.
frontend/app/components/modules/widget/config/popularity/stars/stars.vue Sends collection scope.
frontend/app/components/modules/widget/config/popularity/stars/stars.config.ts Enables collection use.
frontend/app/components/modules/widget/config/popularity/social-mentions/social-mentions.vue Sends collection scope.
frontend/app/components/modules/widget/config/popularity/social-mentions/social-mentions.config.ts Disables collection use.
frontend/app/components/modules/widget/config/popularity/search-queries/search-queries.vue Sends collection scope.
frontend/app/components/modules/widget/config/popularity/search-queries/search-queries.config.ts Enables collection use.
frontend/app/components/modules/widget/config/popularity/press-mentions/press-mentions.vue Sends collection scope.
frontend/app/components/modules/widget/config/popularity/press-mentions/press-mentions.config.ts Disables collection use.
frontend/app/components/modules/widget/config/popularity/package-downloads/package-downloads.vue Sends collection scope.
frontend/app/components/modules/widget/config/popularity/package-downloads/package-downloads.config.ts Enables collection use.
frontend/app/components/modules/widget/config/popularity/package-dependency/package-dependency.vue Sends collection scope.
frontend/app/components/modules/widget/config/popularity/package-dependency/package-dependency.config.ts Enables collection use.
frontend/app/components/modules/widget/config/popularity/mailing-list-messages/mailing-list-messages.vue Sends collection scope.
frontend/app/components/modules/widget/config/popularity/mailing-list-messages/mailing-list-messages.config.ts Enables collection use.
frontend/app/components/modules/widget/config/popularity/github-mentions/github-mentions.vue Sends collection scope.
frontend/app/components/modules/widget/config/popularity/github-mentions/github-mentions.config.ts Disables collection use.
frontend/app/components/modules/widget/config/popularity/forks/forks.vue Sends collection scope.
frontend/app/components/modules/widget/config/popularity/forks/forks.config.ts Enables collection use.
frontend/app/components/modules/widget/config/development/review-time-by-pull-request-size/review-time-by-pull-request-size.vue Sends collection scope.
frontend/app/components/modules/widget/config/development/review-time-by-pull-request-size/review-time-by-pull-request-size.config.ts Enables collection use.
frontend/app/components/modules/widget/config/development/review-efficiency/review-efficiency.vue Sends collection scope.
frontend/app/components/modules/widget/config/development/review-efficiency/review-efficiency.config.ts Enables collection use.
frontend/app/components/modules/widget/config/development/pull-requests/pull-requests.vue Sends collection scope.
frontend/app/components/modules/widget/config/development/pull-requests/pull-requests.config.ts Enables collection use.
frontend/app/components/modules/widget/config/development/patchsets-per-review/patchsets-per-review.vue Sends collection scope.
frontend/app/components/modules/widget/config/development/patchsets-per-review/patchsets-per-review.config.ts Enables collection use.
frontend/app/components/modules/widget/config/development/merge-lead-time/merge-lead-time.vue Sends collection scope.
frontend/app/components/modules/widget/config/development/merge-lead-time/merge-lead-time.config.ts Enables collection use.
frontend/app/components/modules/widget/config/development/median-time-to-review/median-time-to-review.vue Sends collection scope.
frontend/app/components/modules/widget/config/development/median-time-to-review/median-time-to-review.config.ts Enables collection use.
frontend/app/components/modules/widget/config/development/median-time-to-close/median-time-to-close.vue Sends collection scope.
frontend/app/components/modules/widget/config/development/median-time-to-close/median-time-to-close.config.ts Enables collection use.
frontend/app/components/modules/widget/config/development/issues-resolution/issues-resolution.vue Sends collection scope.
frontend/app/components/modules/widget/config/development/issues-resolution/issues-resolution.config.ts Enables collection use.
frontend/app/components/modules/widget/config/development/contributions-outside-work-hours/contributions-outside-work-hours.vue Sends collection scope.
frontend/app/components/modules/widget/config/development/contributions-outside-work-hours/contributions-outside-work-hours.config.ts Enables collection use.
frontend/app/components/modules/widget/config/development/commit-activities/commit-activities.vue Sends collection scope.
frontend/app/components/modules/widget/config/development/commit-activities/commit-activities.config.ts Enables collection use.
frontend/app/components/modules/widget/config/development/code-review-engagement/code-review-engagement.vue Sends collection scope.
frontend/app/components/modules/widget/config/development/code-review-engagement/code-review-engagement.config.ts Enables collection use.
frontend/app/components/modules/widget/config/development/active-days/active-days.vue Sends collection scope.
frontend/app/components/modules/widget/config/development/active-days/active-days.config.ts Enables collection use.
frontend/app/components/modules/widget/config/contributor/retention/retention.vue Sends collection scope.
frontend/app/components/modules/widget/config/contributor/retention/retention.config.ts Enables collection use.
frontend/app/components/modules/widget/config/contributor/organizations-leaderboard/organizations-leaderboard.vue Sends collection scope.
frontend/app/components/modules/widget/config/contributor/organizations-leaderboard/organizations-leaderboard.config.ts Enables collection use.
frontend/app/components/modules/widget/config/contributor/organizations-leaderboard/fragments/organization-leaderboard-drawer.vue Fixes drawer scope.
frontend/app/components/modules/widget/config/contributor/organization-dependency/organization-dependency.vue Sends collection scope.
frontend/app/components/modules/widget/config/contributor/organization-dependency/organization-dependency.config.ts Enables collection use.
frontend/app/components/modules/widget/config/contributor/geographical-distribution/geographical-distribution.vue Sends collection scope.
frontend/app/components/modules/widget/config/contributor/geographical-distribution/geographical-distribution.config.ts Enables collection use.
frontend/app/components/modules/widget/config/contributor/contributors-leaderboard/fragments/contributor-leaderboard-drawer.vue Fixes drawer scope.
frontend/app/components/modules/widget/config/contributor/contributors-leaderboard/contributors-leaderboard.vue Sends collection scope.
frontend/app/components/modules/widget/config/contributor/contributors-leaderboard/contributors-leaderboard.config.ts Enables collection use.
frontend/app/components/modules/widget/config/contributor/contributor-dependency/contributor-dependency.vue Sends collection scope.
frontend/app/components/modules/widget/config/contributor/contributor-dependency/contributor-dependency.config.ts Enables collection use.
frontend/app/components/modules/widget/config/contributor/active-organizations/active-organizations.vue Sends collection scope.
frontend/app/components/modules/widget/config/contributor/active-organizations/active-organizations.config.ts Enables collection use.
frontend/app/components/modules/widget/config/contributor/active-contributors/active-contributors.vue Sends collection scope.
frontend/app/components/modules/widget/config/contributor/active-contributors/active-contributors.config.ts Enables collection use.
frontend/app/components/modules/widget/composables/usePopularityExcludedWidgets.ts Makes empty-data checks scope-aware.
frontend/app/components/modules/widget/components/shared/widget.vue Hides collection benchmarks.
frontend/app/components/modules/widget/components/shared/widget-area.vue Filters collection widgets and SSR queries.
frontend/app/components/modules/widget/components/contributors/fragments/activities-dropdown.vue Loads collection activity filters.
frontend/app/components/modules/project/store/project.store.ts Adds collection-aware derived state.
frontend/app/components/modules/collection/views/collection-popularity.vue Replaces summary cards with widgets.
frontend/app/components/modules/collection/views/collection-development.vue Replaces summary cards with widgets.
frontend/app/components/modules/collection/views/collection-contributors.vue Replaces table with widgets.
frontend/app/components/modules/collection/services/collections.api.service.ts Removes obsolete aggregate APIs.
frontend/app/components/modules/collection/config/collection-links.ts Enables aggregate navigation links.
frontend/app/components/modules/collection/components/details/header.vue Restricts project-only toggle.
frontend/app/components/modules/collection/components/details/collection-metrics-row.vue Stabilizes rendered keys.
frontend/app/components/modules/collection/components/details/collection-menu.vue Adds responsive tabs and date picker.

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

Comment thread frontend/app/components/modules/project/store/project.store.ts
Comment on lines +83 to +87
// Whether this widget can appear on a Collection detail page's Contributors/Popularity/
// Development tabs (aggregated across every project in the collection), in addition to
// individual Project pages. Collections have no per-project `widgets` array to check
// against, so this is an explicit opt-in rather than inherited from project data.
availableInCollection?: boolean;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good catch on the gap, though it's a degraded-UI issue rather than a crash: widget.vue already guards both call sites against a null project - the description block is wrapped in v-if="project" (so it just doesn't render rather than throwing), and getDefaultValue() already does project.value ? defaultValue(project.value) : {}. So collection widgets currently render with no description text and empty computed defaults, but they don't break.

Building proper collection-aware descriptions/defaults would mean touching the WidgetConfig contract and rewriting description/defaultValue for every one of the ~26 availableInCollection widgets individually - a much larger, cross-cutting change than fits in this fix round. Filing this as a scoped follow-up rather than doing it here; happy to prioritize it if the no-description state is worse for users than I'm assuming.

Comment thread frontend/app/components/modules/widget/components/shared/widget.vue
Comment thread frontend/app/pages/collection/details/[slug].vue
Comment thread frontend/server/utils/common.ts Outdated
Comment thread frontend/server/data/tinybird/development/code-review-engagement.ts
- project.store.ts: reset project/collaborationSet when entering
  collection scope, so a client-side nav from a project page no longer
  leaves stale project data behind for widgets that read the store
  directly (description, defaultValue, embed/share).
- widget-menu.vue: disable the Embed menu action in collection scope -
  its URL is built from project.value?.slug, which is undefined there
  and has no collection embed route yet.
- collection/details/[slug].vue: also watch isAggregateWidgetTab (not
  just the query data) in the non-curated redirect guard, since the
  parent layout stays mounted across child-tab navigation and a
  data-only watcher never re-runs the guard for a client-side tab
  switch after data has already loaded.
- common.ts getWidgetScope: reject array values for project/
  collectionSlug (e.g. ?project=a&project=b) instead of silently
  passing the exclusivity check with an array bound to a string-typed
  field.
- collection-contributors-leaderboard.ts: forward activity_types
  (plural) alongside activity_type to both the data and count queries,
  matching the Tinybird query type contract.
- Added missing test coverage: collection-contributors-leaderboard.ts
  had no test file; contributors-dependency.test.ts and
  code-review-engagement.test.ts only covered the project-scoped path,
  not collectionSlug.

Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Copilot AI review requested due to automatic review settings July 17, 2026 10:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 133 out of 133 changed files in this pull request and generated 2 comments.

Comment on lines +102 to +112
if (!dateQueryParams.value.timeRange) {
selectedTimeRangeKey.value = defaultTimeRangeKey;
startDate.value = defaultDateOption?.startDate || null;
endDate.value = defaultDateOption?.endDate || null;

dateQueryParams.value = {
timeRange: selectedTimeRangeKey.value,
start: startDate.value,
end: endDate.value,
};
}
key: 'searchQueries',
availableInCollection: true,
name: 'Search queries volume',
description: (project) => `Search volume for "${project.name}" on Google Search.`,
…velocity IN-1191

Fixes found during Collections v2 prod QA:

- Header metric chips stayed stuck on loading skeletons after refreshing on a
  sub-tab (Contributors/Popularity/Development). The metrics query was never
  awaited in onServerPrefetch, so SSR shipped isMetricsLoading=true and the
  client never re-ran it. Await metricsSuspense() during SSR.
- Selected date range reset to the default when switching between the in-depth
  tabs, because the tab links dropped the route query. Preserve query (timeRange/
  start/end, onlyLFProjects) in collection-menu links, mirroring project-menu.
- Development tab's Issues-resolution and Pull-requests widgets rendered a null
  avgVelocityInDays as a literal 'Avg. velocity: 0 seconds' (collection-scoped
  pipe returns null). Render '—' instead of a fake zero value.

Claude-Session: https://claude.ai/code/session_014Fc86j8FLeWFAs3fDCQs38
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Copilot AI review requested due to automatic review settings July 18, 2026 13:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 133 out of 133 changed files in this pull request and generated 2 comments.

*/
const projectSlug = computed(() => route.params.slug as string);
const projectSlug = computed(() => (isCollectionScope.value ? undefined : (route.params.slug as string)));
const collectionSlug = computed(() => (isCollectionScope.value ? (route.params.slug as string) : undefined));
Comment on lines +186 to +191
// Resolve the header metrics query during SSR too, otherwise on a sub-tab refresh
// (Contributors/Popularity/Development) the server ships isMetricsLoading=true and the
// client never re-runs the query, leaving the header metric chips stuck on skeletons.
// Awaited after the 404 check so a metrics failure never blocks the collection 404 path;
// metrics.ts already degrades gracefully on upstream error, so this can't hang the page.
await metricsSuspense();
…ets IN-1191

The Issues-resolution, Pull-requests and Merge-lead-time server handlers accessed
velocity.data[0].<field> without guarding the array. When Tinybird returns an empty
data array (no resolved issues/PRs in scope — common for collection-scoped or slow
SSR requests on large collections), data[0] is undefined and the property access
threw, dropping the value from the widget response — surfacing as 'Avg. velocity: —'
despite the metric being computable, and merge-lead-time rendering '0 seconds'.

Guard every .data[0] access with optional chaining and a null fallback, and make
avgVelocityInDays nullable end-to-end so the frontend renders '—' honestly instead
of a fabricated 0 or crashing the widget. Verified locally: real values still render
(pytorch issues '1 month', PRs '15 days'), and an empty date window returns null
cleanly with no crash.

Claude-Session: https://claude.ai/code/session_014Fc86j8FLeWFAs3fDCQs38
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Copilot AI review requested due to automatic review settings July 18, 2026 14:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 137 out of 137 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

frontend/app/pages/collection/details/[slug].vue:102

  • A collection URL's explicit date range is never copied into the existing Pinia store. After SPA navigation, this branch only handles the missing-query case, so the date picker mounts with the previous page's range and its immediate watcher overwrites the requested URL. Synchronize the store from dateQueryParams when timeRange is present, and only write defaults when it is absent.

Comment on lines +39 to +53
const currentValue = current?.openedToMergedSeconds ?? null;
const previousValue = previous?.openedToMergedSeconds ?? null;
const currentToReviewAssigned = current?.openedToReviewAssignedSeconds ?? null;
const previousToReviewAssigned = previous?.openedToReviewAssignedSeconds ?? null;
const currentToFirstReview = current?.reviewAssignedToFirstReviewSeconds ?? null;
const previousToFirstReview = previous?.reviewAssignedToFirstReviewSeconds ?? null;
const currentToApproved = current?.firstReviewToApprovedSeconds ?? null;
const previousToApproved = previous?.firstReviewToApprovedSeconds ?? null;
const currentToMerged = current?.approvedToMergedSeconds ?? null;
const previousToMerged = previous?.approvedToMergedSeconds ?? null;

// Only meaningful when both periods have data; comparing against a null (no-data) period would
// be misleading, so treat those as neutral.
const changeType = (curr: number | null, prev: number | null): 'positive' | 'negative' =>
curr !== null && prev !== null && curr > prev ? 'positive' : 'negative';
// Collections have no per-project `widgets` array to check against (see widget-area.vue's
// identical isWidgetShown branch) - availableInCollection already gated these widgets into
// the sidebar, so the only remaining check here is the emptiness one below.
return isCollectionScope || projectWidgets.includes(lfxWidgets[widget]?.key);
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.

2 participants