Skip to content

feat(frontend): merge insight-front into the monorepo - #2223

Merged
aleksdotbar merged 481 commits into
mainfrom
merge/insight-front
Aug 5, 2026
Merged

feat(frontend): merge insight-front into the monorepo#2223
aleksdotbar merged 481 commits into
mainfrom
merge/insight-front

Conversation

@aleksdotbar

@aleksdotbar aleksdotbar commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Frontend source moves here from constructorfabric/insight-front. History preserved — filter-repo --to-subdirectory-filter src/frontend, source a7d4ed01, 473 commits.

Do not squash. Squash flattens the imported history into one commit. The queue is set to MERGE — leave it.

Merge after constructorfabric/insight-front#267. That retires the old image build; until it lands both repos publish insight-front:latest from divergent trees, and latest is the default compose mode.

What lands

  • src/frontend/ — sources. Agent tooling, workflows and duplicate licence files dropped from the tree; history keeps them.
  • ci.ymljs producer: lint, typecheck, both vitest projects, merged coverage into the existing coverage-gate. 2382/2498 lines, gated at the standard 80%. Runs in the Playwright image (browser story tests); tag and digest both pinned.
  • build-images.yml — per-arch frontend build, multi-arch manifest, provenance. publish-chart bumps the subchart appVersion directly; the cross-repo frontend_tag dispatch input is removed. merge-frontend fails loudly on a half-failed matrix so a nonexistent tag can never reach a published chart.
  • Compose reads ./src/frontend. INSIGHT_FRONT_PATH and the wizard clone option are gone.
  • npm dependabot entry ported — the root config had no npm ecosystem.

Image name and tag format unchanged, so charts, values files and environment overlays need no edit.

Not in scope

Issue #1818 Phase 4: transfer open issues, archive insight-front, add the frontend jobs to required checks. code-ranker.yml is dropped with no monorepo equivalent — port it separately if wanted.

Refs #1818

mitasovr and others added 30 commits June 26, 2026 17:25
…nd-global-services-gate

fix(sales-dashboard): wire CRM Pipeline Now + gate dashboard for Global Services
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Replace the flat status + free-form error with a typed AuthStatus where
"re-auth needed" is a status tag, not a predicate derived from magic
strings. Drop isTerminalAuthFailure.

Centralize the redirect in OidcManager.requireReauth(), called from the
401 path and the silent-renew failure, so the 401 check stays at the
fetch boundary and the auth layer never references HTTP status. The gate
is now pure presentation.

Add a reauth_failed state with a retry so a redirect that fails to start
can no longer pin the app behind a permanent overlay.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
From a full-flow review of the auth/reauth path:

- safeReturnUrl: resolve against our origin and echo back only a
  same-origin path+query+hash. The prefix checks let backslash network
  paths (/\host, folded to //host by browsers) through as open redirects.
- signIn: never persist /callback as the return target — a fresh visit
  has no code and loops back into the failure screen.
- fetchWithAuth: only fall back to a dev/impersonation bearer when OIDC
  is disabled, so a URL override can't mint an unsigned alg:none bearer
  while a real OIDC token is briefly null mid-renew.
- root beforeLoad: route first-load through requireReauth so a redirect
  that can't start lands in reauth_failed (retry UI) instead of throwing
  an unhandled rejection out of beforeLoad.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Second-pass review follow-ups:

- fetchWithAuth: mint a dev bearer only when status is "disabled" AND
  reason is "dev_bypass". An unconfigured prod deploy (missing_oidc_config)
  now fails closed instead of letting a URL override forge a bearer.
- callback retry: catch signIn() rejection so a redirect that can't start
  stays contained on the error screen instead of surfacing as an unhandled
  promise rejection.
- types: note that reauth_required is also set by doInit on first load,
  where beforeLoad (not the component tree) performs the redirect.
- tests: cover the status/reason-gated dev-bearer branch and the renewal
  escalation path (access-token expiry -> renewing, silent-renew failure
  -> reauth_required) by exercising the captured oidc-client handlers.

Skipped the addUserUnloaded -> renewing suggestion: with monitorSession
off it only fires via signOut's removeUser, which immediately resets and
redirects; a terminal/trigger state there would either be overwritten or
race the signout redirect.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Hard-fail PRs whose changed lines fall below 80% coverage, while keeping
global coverage a non-failing warning. Single-component repo, so no
per-component routing.

- scripts/ci/diff-coverage.sh: single source of truth wrapping diff-cover
  against the Cobertura report; runs identically locally
  (pnpm test:coverage:diff) and in CI.
- ci.yml: coverage-gate job (PR-only, needs: test) reuses the coverage
  artifact the test job already uploads, installs diff-cover, and fails the
  PR below threshold. Output goes to the job summary (no PR comment, so it
  works on fork PRs with a read-only token). The global irongut summary is
  unchanged (fail_below_min: false).

Coverage is logic-line based on this Vitest 4 / v8 stack; bare JSX markup
lines are not counted (an istanbul trial tracked the identical line set),
so pure-markup diffs have no coverable lines and pass. Documented in the
script.

Follow-up: make coverage-gate a required status check in branch protection.
Signed-off-by: Konstantin Tursunov <konstantin.tursunov@constructor.tech>
Replace the third-party irongut/CodeCoverageSummary action with a
self-contained scripts/ci/coverage-summary.py that renders the global
coverage markdown (overall line/branch + per-area breakdown, 60/80
thresholds) from the Cobertura report — mirroring constructorfabric/insight,
which self-generates its coverage report instead of using an external action.

The summary stays a non-failing warning written to the job summary; the
new-code diff gate remains the only blocking check. Adds
pnpm test:coverage:summary for local parity.

Signed-off-by: Konstantin Tursunov <konstantin.tursunov@constructor.tech>
Mirror constructorfabric/insight's topology: the test job is now a pure
producer (runs coverage, uploads the Cobertura artifact), and a single
`coverage` job consumes it to both render the global coverage summary
(non-failing warning, on push and PR) and enforce the new-code diff gate
(pull requests only). Replaces the separate coverage-gate job.

Signed-off-by: Konstantin Tursunov <konstantin.tursunov@constructor.tech>
diff-cover 10.x deprecated --markdown-report in favor of --format
markdown:<file>; switch to it. Behavior-preserving: console report to
stdout + markdown to the job summary, non-zero exit on failure.

Signed-off-by: Konstantin Tursunov <konstantin.tursunov@constructor.tech>
Replace the diff-coverage.sh wrapper with diff-coverage.py, which runs
diff-cover (--json-report, --fail-under 0) and renders the new-code result
the way constructorfabric/insight's coverage.py does: a per-file table
(New lines / Coverage / Min / Result with check/cross icons) plus the raw
diff-cover output in a <details>, written to the job summary. The script
decides PASS/FAIL itself (overall new-line coverage >= 80%) and exits
non-zero on fail. test:coverage:diff updated accordingly.

Signed-off-by: Konstantin Tursunov <konstantin.tursunov@constructor.tech>
Remove the test:coverage:diff / test:coverage:summary npm scripts that
shelled out to python3. pnpm is now self-contained (Node/vitest only): devs
run 'pnpm test:coverage' and open coverage/index.html. The Python coverage
scripts (coverage-summary.py, diff-coverage.py) are invoked exclusively by
ci.yml to process the coverage artifact pnpm produces; headers say so.

Signed-off-by: Konstantin Tursunov <konstantin.tursunov@constructor.tech>
Renames the gate's name across the workflow, scripts, and report headers
(e.g. report heading is now '## Code coverage gate'). Mechanism descriptions
still note it measures coverage of changed lines, which is what diff-cover does.

Signed-off-by: Konstantin Tursunov <konstantin.tursunov@constructor.tech>
…88f7b

fix(auth): re-auth expired session at runtime
ci: add code coverage gate (diff-cover, 80% diff threshold)
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
aleksdotbar and others added 13 commits August 4, 2026 14:37
The view fetches its own data, so stories drive it through MSW; the
handler aggregates a fact cube, so group-by and filter clicks change
the served series instead of replaying one payload. Export tests assert
the produced CSV text and workbook cells.

Tailwind now runs in the storybook Vitest project — without utilities
every element collapses to content size and recharts never paints.

Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
…tories

test(metric-views): cover timeseries view states in Storybook
Switching the group-by dimension refires the collection query. The
caption follows local state, so asserting it proved nothing and the
legend query ran before the response landed — green locally, red on
slower CI.

Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
…-groupby-race

test(metric-views): await the re-grouped series in the story
…orcement

ci(security): enforce — block on secrets, CRITICAL, and new SAST findings
Seven `pnpm.overrides` entries plus one direct bump. Every range is bounded to
the major already installed, so nothing crosses a major boundary:

  undici           7.25.0  -> 7.29.0    TLS bypass and cross-origin routing in
                                        the SOCKS5 ProxyAgent; WebSocket DoS
  fast-uri          3.1.2  -> 3.1.4     host confusion via backslash authority
                                        and failed IDN canonicalization
  postcss          8.5.14  -> 8.5.23    sourceMappingURL path traversal
  js-yaml           4.1.1  -> 4.3.0     quadratic CPU on merge-key chains
  hono            4.12.18  -> 4.12.32   CORS reflects any Origin with credentials
  brace-expansion   5.0.6  -> 5.0.8     unbounded expansion, OOM crash
  vite (direct)    8.0.11  -> 8.0.16    server.fs.deny bypass on Windows paths

Left alone, deliberately:

brace-expansion 1.x and 2.x. Their fixes, 1.1.17 and 2.1.3, were published on
29 and 28 July; pnpm-workspace.yaml sets minimumReleaseAge to seven days and
pnpm refuses them with ERR_PNPM_NO_MATURE_MATCHING_VERSION. Waiting is the
point of that setting.

uuid 8.3.2. Fixed only from 11.1.1, three majors up, inside a transitive
dependency of exceljs.

Local scan over the lock with dev dependencies included: 19 -> 7.

Signed-off-by: Gregory Gogin <grigoriy.gogin@constructor.tech>
…-images-0e8fc498de

build(deps): Bump node from 24-bookworm-slim to 25-bookworm-slim in the base-images group
fix(deps): clear the fixable HIGH advisories in the lock (#265)
Wires src/frontend into the existing pipelines instead of adding parallel
ones, ahead of the frontend sources landing under that path.

- ci.yml gains a `js` producer (lint, typecheck, both vitest projects
  under merged coverage) feeding the existing coverage-gate, so the
  frontend inherits the required check rather than needing a new one.
  Its Cobertura source root is rewritten to a repo-relative path:
  the producer runs in a container whose workspace differs from the
  gate job's, and neither coverage.py nor diff-cover can resolve an
  absolute root across that boundary.
- components.py registers the component with an `overall_min` below the
  default; the new-code gate still applies in full.
- build-images.yml builds and publishes ghcr.io/constructorfabric/insight-front
  per-arch with a merged manifest and provenance, and publish-chart bumps
  the subchart appVersion directly. The cross-repo `frontend_tag` dispatch
  input is removed.
- merge-frontend fails loudly when a build leg fails, so a half-failed
  matrix cannot pin an appVersion to a manifest that was never assembled.
- Compose reads ./src/frontend; INSIGHT_FRONT_PATH and the wizard's clone
  option are gone.

Refs #1818

Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
The imported tree carried its own agent tooling, workflows and licence
files. History keeps all of it; the working tree keeps only what the
monorepo does not already provide.

- Agent tooling (cypilot, .agents, .claude, .codex, .windsurf) gives way
  to .cf-studio. .claude/skills/shadcn was a symlink into .agents, so
  both had to go together.
- .github/: workflows are superseded by ci.yml and build-images.yml;
  CODEOWNERS and copilot-instructions.md are only read at the repository
  root, so they were inert at this depth. The npm dependabot entry is
  ported to the root config, which had no npm ecosystem.
- scripts/ci/ reimplemented scripts/ci/coverage.py.
- LICENSE, NOTICE, SECURITY.md, docker-compose.yml and .semgrepignore
  duplicate their repository-root counterparts.

.gitignore keeps .env.example tracked: the root `.env.*` rule would
otherwise ignore it, and the root `!.env.*.example` negation does not
match that name.

Refs #1818

Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Running the suite shows the frontend clears the repository-wide 80% line
floor with margin, so it needs no exemption: 2382/2498 lines with both
vitest projects merged, which is what test:coverage:ci collects.

Drops the per-component `overall_min` override introduced alongside the
frontend component, and the machinery that read it — with no consumer it
was speculative, and a lowered floor would have masked the browser
project silently dropping out of the merged report.

Also groups the `changes` job's step-output redirects, which shellcheck
flagged (SC2129) once a third one was added.

Refs #1818

Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
@aleksdotbar
aleksdotbar requested a review from a team as a code owner August 5, 2026 06:56
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 311 files, which is 211 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

Usage-priced reviews support at most 300 files.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6ac4c4d5-0c39-497d-ba31-5765999d2817

📥 Commits

Reviewing files that changed from the base of the PR and between 9ab0549 and 6947967.

⛔ Files ignored due to path filters (1)
  • src/frontend/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (311)
  • .env.compose.example
  • .github/dependabot.yml
  • .github/workflows/build-images.yml
  • .github/workflows/ci.yml
  • .github/workflows/scripts/bump-service-appversions.sh
  • .github/workflows/trivy-images.yml
  • .semgrepignore
  • CONTRIBUTING.md
  • README.md
  • deploy/compose/insight-init.sh
  • docker-compose.yml
  • docs/TESTING.md
  • docs/components/backend/specs/DESIGN.md
  • docs/components/deployment/specs/DESIGN.md
  • docs/components/deployment/specs/PRD.md
  • scripts/ci/changed.py
  • scripts/ci/components.py
  • scripts/ci/coverage.py
  • src/frontend/.dockerignore
  • src/frontend/.env.example
  • src/frontend/.gitignore
  • src/frontend/.nvmrc
  • src/frontend/.prettierignore
  • src/frontend/.prettierrc
  • src/frontend/.storybook/main.ts
  • src/frontend/.storybook/manager.ts
  • src/frontend/.storybook/preview.tsx
  • src/frontend/AGENTS.md
  • src/frontend/Dockerfile
  • src/frontend/README.md
  • src/frontend/components.json
  • src/frontend/docker-entrypoint.sh
  • src/frontend/docs/release-notes.md
  • src/frontend/docs/testing/storybook-component-tests.md
  • src/frontend/eslint.config.js
  • src/frontend/index.html
  • src/frontend/nginx/default.conf.template
  • src/frontend/nginx/security-headers.conf
  • src/frontend/package.json
  • src/frontend/pnpm-workspace.yaml
  • src/frontend/public/mockServiceWorker.js
  • src/frontend/src/api/analytics-client.ts
  • src/frontend/src/api/fetch-with-auth.test.ts
  • src/frontend/src/api/fetch-with-auth.ts
  • src/frontend/src/api/identity-client.test.ts
  • src/frontend/src/api/identity-client.ts
  • src/frontend/src/api/metric-definitions-client.test.ts
  • src/frontend/src/api/metric-definitions-client.ts
  • src/frontend/src/api/metric-drilldown-client.test.ts
  • src/frontend/src/api/metric-drilldown-client.ts
  • src/frontend/src/api/metric-results-client.test.ts
  • src/frontend/src/api/metric-results-client.ts
  • src/frontend/src/api/period-to-date-range.test.ts
  • src/frontend/src/api/period-to-date-range.ts
  • src/frontend/src/api/saved-queries-client.test.ts
  • src/frontend/src/api/saved-queries-client.ts
  • src/frontend/src/auth/auth-error.test.ts
  • src/frontend/src/auth/auth-error.ts
  • src/frontend/src/auth/auth-store.ts
  • src/frontend/src/auth/index.ts
  • src/frontend/src/auth/override.test.ts
  • src/frontend/src/auth/override.ts
  • src/frontend/src/auth/refresh.test.ts
  • src/frontend/src/auth/refresh.ts
  • src/frontend/src/auth/session-scope.test.ts
  • src/frontend/src/auth/session-scope.ts
  • src/frontend/src/auth/session.test.ts
  • src/frontend/src/auth/session.ts
  • src/frontend/src/auth/types.ts
  • src/frontend/src/auth/use-auth.test.ts
  • src/frontend/src/auth/use-auth.ts
  • src/frontend/src/auth/use-viewer.test.ts
  • src/frontend/src/auth/use-viewer.ts
  • src/frontend/src/components/app-boot-spinner.tsx
  • src/frontend/src/components/app-error-boundary.tsx
  • src/frontend/src/components/app-sidebar.test.tsx
  • src/frontend/src/components/app-sidebar.tsx
  • src/frontend/src/components/auth-gate.test.tsx
  • src/frontend/src/components/auth-gate.tsx
  • src/frontend/src/components/error-fallback.test.tsx
  • src/frontend/src/components/error-fallback.tsx
  • src/frontend/src/components/full-screen-loading.tsx
  • src/frontend/src/components/ic-view-toggle.test.tsx
  • src/frontend/src/components/ic-view-toggle.tsx
  • src/frontend/src/components/login-error.tsx
  • src/frontend/src/components/metric-evidence-context.ts
  • src/frontend/src/components/metric-evidence-dialog-provider.test.tsx
  • src/frontend/src/components/metric-evidence-dialog-provider.tsx
  • src/frontend/src/components/metric-evidence-dialog.test.tsx
  • src/frontend/src/components/metric-evidence-dialog.tsx
  • src/frontend/src/components/metric-evidence-table.test.tsx
  • src/frontend/src/components/metric-evidence-table.tsx
  • src/frontend/src/components/mock-banner.test.tsx
  • src/frontend/src/components/mock-banner.tsx
  • src/frontend/src/components/sidebar-settings.test.tsx
  • src/frontend/src/components/sidebar-settings.tsx
  • src/frontend/src/components/theme-provider.test.tsx
  • src/frontend/src/components/theme-provider.tsx
  • src/frontend/src/components/theme-switcher.test.tsx
  • src/frontend/src/components/theme-switcher.tsx
  • src/frontend/src/components/ui/alert.tsx
  • src/frontend/src/components/ui/avatar.tsx
  • src/frontend/src/components/ui/badge.tsx
  • src/frontend/src/components/ui/button.tsx
  • src/frontend/src/components/ui/calendar.tsx
  • src/frontend/src/components/ui/card.tsx
  • src/frontend/src/components/ui/chart.tsx
  • src/frontend/src/components/ui/checkbox.tsx
  • src/frontend/src/components/ui/collapsible.tsx
  • src/frontend/src/components/ui/dialog.tsx
  • src/frontend/src/components/ui/dropdown-menu.tsx
  • src/frontend/src/components/ui/empty.tsx
  • src/frontend/src/components/ui/input.tsx
  • src/frontend/src/components/ui/label.tsx
  • src/frontend/src/components/ui/popover.tsx
  • src/frontend/src/components/ui/preview-card.tsx
  • src/frontend/src/components/ui/select.tsx
  • src/frontend/src/components/ui/separator.tsx
  • src/frontend/src/components/ui/sheet.tsx
  • src/frontend/src/components/ui/sidebar.tsx
  • src/frontend/src/components/ui/skeleton.tsx
  • src/frontend/src/components/ui/sonner.tsx
  • src/frontend/src/components/ui/spinner.tsx
  • src/frontend/src/components/ui/switch.tsx
  • src/frontend/src/components/ui/table.tsx
  • src/frontend/src/components/ui/tabs.tsx
  • src/frontend/src/components/ui/textarea.tsx
  • src/frontend/src/components/ui/toggle-group.tsx
  • src/frontend/src/components/ui/toggle.tsx
  • src/frontend/src/components/ui/tooltip.tsx
  • src/frontend/src/components/view-as-banner.tsx
  • src/frontend/src/components/widgets/centered-spinner.tsx
  • src/frontend/src/components/widgets/coming-soon.test.tsx
  • src/frontend/src/components/widgets/coming-soon.tsx
  • src/frontend/src/components/widgets/dashboard/dashboard-empty-state.test.tsx
  • src/frontend/src/components/widgets/dashboard/dashboard-empty-state.tsx
  • src/frontend/src/components/widgets/dashboard/dashboard-header.tsx
  • src/frontend/src/components/widgets/dashboard/group-drilldown-sheet.test.tsx
  • src/frontend/src/components/widgets/dashboard/group-drilldown-sheet.tsx
  • src/frontend/src/components/widgets/dashboard/ic-needs-attention.test.tsx
  • src/frontend/src/components/widgets/dashboard/ic-needs-attention.tsx
  • src/frontend/src/components/widgets/dashboard/kpi-tile.stories.tsx
  • src/frontend/src/components/widgets/dashboard/kpi-tile.test.tsx
  • src/frontend/src/components/widgets/dashboard/kpi-tile.tsx
  • src/frontend/src/components/widgets/dashboard/members-grid.test.tsx
  • src/frontend/src/components/widgets/dashboard/members-grid.tsx
  • src/frontend/src/components/widgets/dashboard/members-overview.test.tsx
  • src/frontend/src/components/widgets/dashboard/members-overview.tsx
  • src/frontend/src/components/widgets/dashboard/metric-sublabel.tsx
  • src/frontend/src/components/widgets/dashboard/team-members-attention.test.tsx
  • src/frontend/src/components/widgets/dashboard/team-members-attention.tsx
  • src/frontend/src/components/widgets/dashboard/triage-list.test.tsx
  • src/frontend/src/components/widgets/dashboard/triage-list.tsx
  • src/frontend/src/components/widgets/group-card-empty.tsx
  • src/frontend/src/components/widgets/metric-views/chart-empty.tsx
  • src/frontend/src/components/widgets/metric-views/collection-drilldown.test.tsx
  • src/frontend/src/components/widgets/metric-views/collection-drilldown.tsx
  • src/frontend/src/components/widgets/metric-views/dimension-series.test.ts
  • src/frontend/src/components/widgets/metric-views/dimension-series.ts
  • src/frontend/src/components/widgets/metric-views/metric-breakdown.test.tsx
  • src/frontend/src/components/widgets/metric-views/metric-breakdown.tsx
  • src/frontend/src/components/widgets/metric-views/metric-card-actions.tsx
  • src/frontend/src/components/widgets/metric-views/metric-group-card.test.tsx
  • src/frontend/src/components/widgets/metric-views/metric-group-card.tsx
  • src/frontend/src/components/widgets/metric-views/metric-histogram.test.tsx
  • src/frontend/src/components/widgets/metric-views/metric-histogram.tsx
  • src/frontend/src/components/widgets/metric-views/metric-summary-card.test.tsx
  • src/frontend/src/components/widgets/metric-views/metric-summary-card.tsx
  • src/frontend/src/components/widgets/metric-views/metric-timeseries-chart-model.ts
  • src/frontend/src/components/widgets/metric-views/metric-timeseries-chart.tsx
  • src/frontend/src/components/widgets/metric-views/metric-timeseries-chrome.tsx
  • src/frontend/src/components/widgets/metric-views/metric-timeseries-csv.ts
  • src/frontend/src/components/widgets/metric-views/metric-timeseries-export-browser.test.ts
  • src/frontend/src/components/widgets/metric-views/metric-timeseries-export.test.ts
  • src/frontend/src/components/widgets/metric-views/metric-timeseries-export.ts
  • src/frontend/src/components/widgets/metric-views/metric-timeseries-model.test.ts
  • src/frontend/src/components/widgets/metric-views/metric-timeseries-model.ts
  • src/frontend/src/components/widgets/metric-views/metric-timeseries-presentation.ts
  • src/frontend/src/components/widgets/metric-views/metric-timeseries-presentations.test.tsx
  • src/frontend/src/components/widgets/metric-views/metric-timeseries-table-model.ts
  • src/frontend/src/components/widgets/metric-views/metric-timeseries-table.tsx
  • src/frontend/src/components/widgets/metric-views/metric-timeseries-view.stories.tsx
  • src/frontend/src/components/widgets/metric-views/metric-timeseries-view.test.tsx
  • src/frontend/src/components/widgets/metric-views/metric-timeseries-view.tsx
  • src/frontend/src/components/widgets/metric-views/metric-timeseries-xlsx.ts
  • src/frontend/src/components/widgets/metric-views/metric-timeseries.story-fixtures.ts
  • src/frontend/src/components/widgets/metric-views/metric-timeseries.test-fixtures.ts
  • src/frontend/src/components/widgets/metric-views/peer-comparison.test.tsx
  • src/frontend/src/components/widgets/metric-views/peer-comparison.tsx
  • src/frontend/src/components/widgets/metric-views/peer-story.test.tsx
  • src/frontend/src/components/widgets/metric-views/peer-story.tsx
  • src/frontend/src/components/widgets/metric-views/team-collection-drilldown.test.tsx
  • src/frontend/src/components/widgets/metric-views/team-collection-drilldown.tsx
  • src/frontend/src/components/widgets/metric-views/team-metric-group-card.test.tsx
  • src/frontend/src/components/widgets/metric-views/team-metric-group-card.tsx
  • src/frontend/src/components/widgets/period-selector-bar.test.tsx
  • src/frontend/src/components/widgets/period-selector-bar.tsx
  • src/frontend/src/components/widgets/query-console/query-detail.test.tsx
  • src/frontend/src/components/widgets/query-console/query-detail.tsx
  • src/frontend/src/components/widgets/query-console/query-editor-dialog.test.tsx
  • src/frontend/src/components/widgets/query-console/query-editor-dialog.tsx
  • src/frontend/src/components/widgets/query-console/query-results.test.tsx
  • src/frontend/src/components/widgets/query-console/query-results.tsx
  • src/frontend/src/config/constants.ts
  • src/frontend/src/hooks/use-local-storage-state.test.tsx
  • src/frontend/src/hooks/use-local-storage-state.ts
  • src/frontend/src/hooks/use-mobile.ts
  • src/frontend/src/hooks/use-period.test.ts
  • src/frontend/src/hooks/use-period.ts
  • src/frontend/src/hooks/use-settings.ts
  • src/frontend/src/i18n/index.ts
  • src/frontend/src/i18n/resources.ts
  • src/frontend/src/i18n/types.d.ts
  • src/frontend/src/index.css
  • src/frontend/src/lib/download.ts
  • src/frontend/src/lib/format.test.ts
  • src/frontend/src/lib/format.ts
  • src/frontend/src/lib/insight/attention.test.ts
  • src/frontend/src/lib/insight/attention.ts
  • src/frontend/src/lib/insight/get-initials.test.ts
  • src/frontend/src/lib/insight/get-initials.ts
  • src/frontend/src/lib/insight/groups.test.ts
  • src/frontend/src/lib/insight/groups.ts
  • src/frontend/src/lib/insight/identity-tree.test.ts
  • src/frontend/src/lib/insight/identity-tree.ts
  • src/frontend/src/lib/insight/kpi-row.test.ts
  • src/frontend/src/lib/insight/kpi-row.ts
  • src/frontend/src/lib/insight/peer-status.test.ts
  • src/frontend/src/lib/insight/peer-status.ts
  • src/frontend/src/lib/insight/team-metrics.test.ts
  • src/frontend/src/lib/insight/team-metrics.ts
  • src/frontend/src/lib/metrics/collection.test.ts
  • src/frontend/src/lib/metrics/collection.ts
  • src/frontend/src/lib/metrics/delta.test.ts
  • src/frontend/src/lib/metrics/delta.ts
  • src/frontend/src/lib/metrics/entity.test.ts
  • src/frontend/src/lib/metrics/entity.ts
  • src/frontend/src/lib/metrics/gap.test.ts
  • src/frontend/src/lib/metrics/gap.ts
  • src/frontend/src/lib/metrics/peer-standing.test.ts
  • src/frontend/src/lib/metrics/peer-standing.ts
  • src/frontend/src/lib/metrics/peer-story.test.ts
  • src/frontend/src/lib/metrics/peer-story.ts
  • src/frontend/src/lib/metrics/shares.test.ts
  • src/frontend/src/lib/metrics/shares.ts
  • src/frontend/src/lib/metrics/timeseries-chart.ts
  • src/frontend/src/lib/metrics/timeseries-table.ts
  • src/frontend/src/lib/peers.test.ts
  • src/frontend/src/lib/peers.ts
  • src/frontend/src/lib/query-console/api-error.test.ts
  • src/frontend/src/lib/query-console/api-error.ts
  • src/frontend/src/lib/query-console/result-shape.test.ts
  • src/frontend/src/lib/query-console/result-shape.ts
  • src/frontend/src/lib/scoring.test.ts
  • src/frontend/src/lib/scoring.ts
  • src/frontend/src/lib/series-colors.test.ts
  • src/frontend/src/lib/series-colors.ts
  • src/frontend/src/lib/status.test.ts
  • src/frontend/src/lib/status.ts
  • src/frontend/src/lib/utils.ts
  • src/frontend/src/locales/en/translation.json
  • src/frontend/src/main.tsx
  • src/frontend/src/mocks/browser.ts
  • src/frontend/src/mocks/handlers.ts
  • src/frontend/src/mocks/metric-results-factory.ts
  • src/frontend/src/mocks/metric-results-fixtures.ts
  • src/frontend/src/mocks/registry.ts
  • src/frontend/src/queries/ic-dashboard.test.ts
  • src/frontend/src/queries/ic-dashboard.ts
  • src/frontend/src/queries/member-grid.test.tsx
  • src/frontend/src/queries/member-grid.ts
  • src/frontend/src/queries/metric-definitions.test.ts
  • src/frontend/src/queries/metric-definitions.ts
  • src/frontend/src/queries/metric-results.test.tsx
  • src/frontend/src/queries/metric-results.ts
  • src/frontend/src/queries/saved-queries.test.tsx
  • src/frontend/src/queries/saved-queries.ts
  • src/frontend/src/query-client.ts
  • src/frontend/src/routeTree.gen.ts
  • src/frontend/src/router.ts
  • src/frontend/src/routes/-__root.test.tsx
  • src/frontend/src/routes/__root.tsx
  • src/frontend/src/routes/ic.$person.index.tsx
  • src/frontend/src/routes/ic.$person.personal.tsx
  • src/frontend/src/routes/ic.$person.team.tsx
  • src/frontend/src/routes/ic.$person.tsx
  • src/frontend/src/routes/index.tsx
  • src/frontend/src/routes/metrics.tsx
  • src/frontend/src/routes/queries.tsx
  • src/frontend/src/routes/whats-new.tsx
  • src/frontend/src/screens/dashboard.test.tsx
  • src/frontend/src/screens/dashboard.tsx
  • src/frontend/src/screens/metric-definitions.test.tsx
  • src/frontend/src/screens/metric-definitions.tsx
  • src/frontend/src/screens/query-console.test.tsx
  • src/frontend/src/screens/query-console.tsx
  • src/frontend/src/screens/team-view.test.tsx
  • src/frontend/src/screens/team-view.tsx
  • src/frontend/src/screens/whats-new.test.tsx
  • src/frontend/src/screens/whats-new.tsx
  • src/frontend/src/test/session.ts
  • src/frontend/src/test/setup.ts
  • src/frontend/src/test/storybook/test-router.ts
  • src/frontend/src/test/storybook/theme-decorators.ts
  • src/frontend/src/test/storybook/with-providers.tsx
  • src/frontend/src/types/insight.ts
  • src/frontend/tsconfig.app.json
  • src/frontend/tsconfig.json
  • src/frontend/tsconfig.node.json
  • src/frontend/vite.config.ts
  • src/frontend/vitest.config.ts

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Comment thread src/frontend/Dockerfile Fixed
The job runs in a container, where the step shell is `sh`; dash rejects
`set -o pipefail` and the step exited 2 after a green test run. Nothing
in the script relies on it — the one pipe is tested through `if !`.

Refs #1818

Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
node:25 ships neither corepack nor pnpm, so `corepack enable` exits 127
and the image build fails. Corepack is not installed to restore it: its
package claims the `yarn`/`yarnpkg` bins the image already holds, so it
would need `--force` to clobber them.

pnpm is installed directly at the version `packageManager` declares,
keeping package.json the single source of truth.

Refs #1818

Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Cut restatement and over-long rationale down to the parts that are not
derivable from the code they sit above.

Refs #1818

Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Comment thread src/frontend/Dockerfile
@@ -0,0 +1,46 @@
FROM node:25-bookworm-slim AS builder
@aleksdotbar
aleksdotbar enabled auto-merge August 5, 2026 07:33
@aleksdotbar
aleksdotbar added this pull request to the merge queue Aug 5, 2026
Merged via the queue into main with commit b1048fe Aug 5, 2026
49 of 51 checks passed
@aleksdotbar
aleksdotbar deleted the merge/insight-front branch August 5, 2026 08:44
mitasovr pushed a commit to mitasovr/insight that referenced this pull request Aug 5, 2026
…ctorfabric#1977)

Port of the constructorfabric#1977 fix into the monorepo FE (`src/frontend`, merged from
insight-front by constructorfabric#2223); supersedes insight-front#268.

The shared metric formatters typed `v: number` with no internal null
guard, so `Math.round(null) === 0` meant a single forgetful caller could
silently render a fabricated 0 for missing data. Widen
`formatMetricNumber`/`formatMetricValue` to `number | null | undefined`
and return `NO_METRIC_VALUE` ("—") for null/undefined/non-finite — a real
0 still formats as 0. The "no data, not zero" guarantee is now structural
rather than left to each caller (defense in depth; existing guarded call
sites are unaffected).

Verified: `pnpm exec vitest run src/lib/format.test.ts` 6/6 pass;
`pnpm typecheck` clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Anton Zelenov <antonz@constructor.tech>
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.

8 participants