Skip to content

feat(sponsor-reports): port Sponsor Reports FE into Summit Admin (Sponsors → Reports)#997

Open
caseylocker wants to merge 49 commits into
masterfrom
feature/sponsor-reports-port
Open

feat(sponsor-reports): port Sponsor Reports FE into Summit Admin (Sponsors → Reports)#997
caseylocker wants to merge 49 commits into
masterfrom
feature/sponsor-reports-port

Conversation

@caseylocker

@caseylocker caseylocker commented Jun 25, 2026

Copy link
Copy Markdown

ref: https://app.clickup.com/t/86bak754x

Ports the two sponsor reports (Purchase Details + Sponsor Assets, incl. the per-sponsor asset drill-down) out of sponsor-services and into summit-admin as an admin-only "Reports" sub-item under the Sponsors menu, reading the in-context summit. Backend (sponsor-reports-api) is unchanged. This is Phase 1 — parity port; the JP-pivot logistics/finance features are Phase 2 follow-ons.

What's included

  • Placement & access: Sponsors → Reports sub-item; layout wrapped Restrict(withRouter(SponsorReportsLayout), "admin-sponsors") — an explicit admin-only gate that reuses the existing admin-sponsors key and closes the direct-URL gap for the reports area. Summit is in-context (summit picker + /reports/summits dropped).
  • Re-implementation on summit-admin's stack (not a copy of the source): react-redux 5 connect() (no hooks), react-router 4 withRouter (no v6 hooks), i18n-react T.translate (no react-i18next), uicore MuiTable (no @mui/x-data-grid), CSV via uicore getCSV.
  • Purchase Details: OrdersTable rebuilt on MuiTable; formatCheckoutTime handles both ISO and epoch; Total Refunded tile renders only when summary.total_refunded != null (auto-appears once that field ships — no FE change needed).
  • Sponsor Assets: group-by sponsor/component views + per-sponsor drill-down; hard-coded drill-down links rewired to /app/summits/:summitId/sponsors/reports/sponsor-assets/sponsors/:sponsorId.
  • Landing with two report cards; breadcrumb trail Sponsors → Reports → <page>.
  • Env: SPONSOR_REPORTS_API_URL injected in app.js + .env/.env.example.

Tests / quality

  • yarn jest: 915 passing, 0 regressions vs master. yarn lint: 0 new errors (only the repo's pre-existing prop-types warnings).
  • Reviewed per-task (opus) + whole-branch (opus) + an independent Codex pass — all clean (0 Critical / 0 Important).

Deploy note (not blocking this PR)

Backend auth wiring (D5) is done on dev — repointed the deployed sponsor-reports-api scope env to reports/all + added the admin groups; verified both reports load real data on dev. Staging/prod need the same env at launch (tracked in the ClickUp ticket). No IDP change was needed (summit-admin already carries reports/all).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a new Sponsor Reports area in the app, including a landing page, Purchase Details (Orders + Line Items), Sponsor Assets, and sponsor drill-down pages.
    • Introduced filtering, grouping, pagination, status rollups, and CSV export with print-friendly reporting layout.
  • Bug Fixes
    • Improved handling of loading, empty, validation, and read-error states, including clearer behavior when CSV export is disabled.
    • Added more robust display formatting for dates, currency, and rich-text content.

caseylocker and others added 10 commits June 24, 2026 16:36
…nsors

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tCSV)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… refund tile)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…gial SORT_FIELD_MAP

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…utes)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace placeholder with two MUI card links (Purchase Details,
Sponsor Assets); add breadcrumb; add landing_title i18n key;
update layout test for card assertions; 6 new tests, 912 total.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…, prop, route test

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…EAD_ERROR

Remove *_EXPORT_DISABLED action constants (purchase-details, sponsor-asset, drilldown)
and their dead reducer arms. Route onExportDisabled → READ_ERROR in getPurchaseDetailsReport
and getSponsorAssetReport thunks (mirrors the already-fixed drilldown), so a 503
export-disabled on a read path clears loading via the same arm as not-found/unauthorized.
Remove exportDisabled and exportError from all three reducer DEFAULT_STATEs, drop
disabled={exportDisabled} from all three ExportCsvButton usages, and clean up all test
references to the deleted constants and unreachable state. Add 503 export-disabled coverage
for getPurchaseDetailsReport and getSponsorAssetReport matching the existing drilldown tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- SummaryPanel tile labels overline -> subtitle1 (bigger; both reports)
- GroupByToggle small -> medium + larger button text
- SponsorReportsLayout renders a persistent Reports crumb + per-route page
  crumb so the trail reads .../Sponsors/Reports/<page> (was dropping to
  .../Sponsors on sub-pages); landing's own crumb removed (no dup)
- Sponsor Assets: gap between Report Filters and summary cards
- tests updated (landing breadcrumb moved to layout; layout trail test added)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

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
📝 Walkthrough

Walkthrough

Adds sponsor reports configuration, Redux wiring, shared report UI, routes, and two sponsor report pages with supporting utilities and tests.

Changes

Sponsor reports feature

Layer / File(s) Summary
Helpers and query shaping
src/app.js, .env.example, src/utils/*, src/utils/__tests__/*, src/hooks/usePrint.js, src/hooks/__tests__/usePrint.test.js
Environment config, report error/query utilities, formatting helpers, manifest grouping, and hook support are added with tests.
Async actions
src/actions/sponsor-reports-actions.js, src/actions/__tests__/sponsor-reports-actions.test.js
Sponsor report thunks fetch purchase details, purchase-detail lines, sponsor assets, and drilldown data and route read, validation, export-disabled, and reauth outcomes.
Reducers and store
src/reducers/sponsors/*, src/reducers/sponsors/__tests__/*, src/store.js
New reducer slices track purchase-details, purchase-details lines, sponsor-asset, and drilldown state, and the store persists the corresponding keys.
Report controls and print support
src/components/sponsors/reports/{ReportShell,FilterBar,ExportCsvButton,GroupByToggle,ReportViewToggle}.js, src/components/sponsors/reports/report-print.css, src/components/sponsors/reports/__tests__/{ReportShell,FilterBar,ExportCsvButton,GroupByToggle,ReportViewToggle}.test.js
Reusable report shell, filters, CSV export, grouping toggles, view toggle, and print stylesheet are added with tests.
Status and display primitives
src/components/sponsors/reports/{statusTone,StatusPill,StatusRollupChips,SummaryPanel,TierBadge,SponsorAvatar}.js, src/components/sponsors/reports/__tests__/{StatusPill,StatusRollupChips,SummaryPanel,TierBadge,SponsorAvatar}.test.js
Status tone mapping, pills, rollup chips, summary tiles, tier badges, and sponsor avatars are added with tests.
Tables and line manifests
src/components/sponsors/reports/{OrdersTable,LinesManifestView}.js, src/components/sponsors/reports/__tests__/{OrdersTable,LinesManifestView}.test.js
Orders tables and line manifests render report data and add view-specific tests.
Grouped sponsor and component cards
src/components/sponsors/reports/{GroupBySponsorView,GroupByComponentView}.js, src/components/sponsors/reports/__tests__/{GroupBySponsorView,GroupByComponentView}.test.js
Sponsor and component grouped cards render report data and add view-specific tests.
Navigation and landing
src/components/menu/menu-definition.js, src/i18n/en.json, src/layouts/sponsor-layout.js, src/layouts/sponsor-reports-layout.js, src/layouts/__tests__/sponsor-reports-layout.test.js, src/pages/sponsors/sponsor-reports/reports-landing-page/*
The sponsor reports menu entry, translations, routes, breadcrumbs, landing cards, and layout tests are added.
Purchase details page
src/pages/sponsors/sponsor-reports/purchase-details-report-page/*
The purchase-details page builds report queries from filters, sort, and pagination, renders summary tiles and actions, and dispatches report and filter thunks with validation handling.
Sponsor asset pages
src/pages/sponsors/sponsor-reports/{sponsor-asset-report-page,sponsor-asset-drilldown-page}/*
The sponsor-asset report and drilldown pages load grouped data, render sponsor and component views, export CSV, and show per-module content states.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • smarcet
  • martinquiroga-exo

Poem

🐰 I sniffed the carrots, crisp and bright,
and hopped through reports by day and night.
CSV trails and badges in sight,
sponsor cards gleam soft and light.
Hop-hop, these changes feel just right!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: porting Sponsor Reports into Summit Admin under Sponsors → Reports.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/sponsor-reports-port

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
src/pages/sponsors/sponsor-reports/__tests__/routing.test.js (1)

33-65: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Test replicates route ordering instead of asserting on the real layout.

Because renderSwitch hand-copies the <Switch> table rather than mounting sponsor-reports-layout.js, it validates React Router's behavior, not the production ordering. If the real layout's route order drifts, this test won't catch it. The drilldown-vs-landing ordering is already covered against the real component in sponsor-reports-layout.test.js, so this can either be removed or driven through the actual layout to guard against regressions.

🤖 Prompt for 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.

In `@src/pages/sponsors/sponsor-reports/__tests__/routing.test.js` around lines 33
- 65, This test is duplicating the route table instead of exercising the real
sponsor-reports layout, so it won’t catch regressions in the production
ordering. Update the routing coverage to mount sponsor-reports-layout.js
directly (using the existing SponsorReportsLayout or equivalent wrapper) instead
of the local renderSwitch Switch copy, or remove this redundant spec if
sponsor-reports-layout.test.js already covers the drilldown-vs-list ordering.
src/components/sponsors/reports/FilterBar.js (1)

28-29: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

draft won't re-sync if the value prop changes after mount.

useState(value) only seeds the initial state; later changes to the value prop (e.g. an external filter reset) won't be reflected in draft. Current flows keep parent/child in sync manually (Apply commits the draft, Clear resets both to {}), so there's no active bug, but this coupling is fragile if value ever becomes externally driven. Consider syncing via an effect keyed on value if that scenario is anticipated.

🤖 Prompt for 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.

In `@src/components/sponsors/reports/FilterBar.js` around lines 28 - 29, The local
draft state in FilterBar is only initialized from the value prop and will not
update if that prop changes later. If external changes to value are expected,
add synchronization in FilterBar using an effect keyed on value so setDraft
stays aligned; keep the update helper unchanged and make sure the draft state
refreshes whenever the incoming value object changes.
src/reducers/sponsors/__tests__/sponsor-reports-reducers.test.js (1)

110-124: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for PURCHASE_DETAILS_VALIDATION_CLEAR.

This file validates the set path for validationError, but not the clear action branch; add one test to prevent regressions in inline/toast reset flow.

🤖 Prompt for 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.

In `@src/reducers/sponsors/__tests__/sponsor-reports-reducers.test.js` around
lines 110 - 124, Add a test for the PURCHASE_DETAILS_VALIDATION_CLEAR branch in
purchaseDetailsReducer to cover the reset path for validationError and loading
state. Use the existing PURCHASE_DETAILS_VALIDATION_ERROR test in
sponsor-reports-reducers.test.js as a guide, but assert that dispatching
PURCHASE_DETAILS_VALIDATION_CLEAR clears validationError without replacing the
current data/body so the inline/toast reset flow stays protected.
🤖 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 `@src/actions/sponsor-reports-actions.js`:
- Around line 34-54: Add an early guard in getPurchaseDetailsReport and
getPurchaseDetailsFilters to check that currentSummit and currentSummit.id exist
before calling dispatch(startLoading()) or base(currentSummit.id). If the summit
is missing, exit the thunk immediately (optionally dispatch a validation/error
action) so the synchronous throw never happens and stopLoading() is not skipped.
Keep the fix localized in sponsor-reports-actions.js and use the existing thunk
names to update both purchase-details actions consistently.

In `@src/utils/reports-money.js`:
- Around line 10-13: Update formatUsd in reports-money.js so blank or
whitespace-only strings are treated as missing values instead of being converted
by Number(). Add an explicit check before parsing string input (for example in
the formatUsd path) to return "—" when the value is empty after trimming, while
keeping the existing USD.format behavior for valid numeric values.

---

Nitpick comments:
In `@src/components/sponsors/reports/FilterBar.js`:
- Around line 28-29: The local draft state in FilterBar is only initialized from
the value prop and will not update if that prop changes later. If external
changes to value are expected, add synchronization in FilterBar using an effect
keyed on value so setDraft stays aligned; keep the update helper unchanged and
make sure the draft state refreshes whenever the incoming value object changes.

In `@src/pages/sponsors/sponsor-reports/__tests__/routing.test.js`:
- Around line 33-65: This test is duplicating the route table instead of
exercising the real sponsor-reports layout, so it won’t catch regressions in the
production ordering. Update the routing coverage to mount
sponsor-reports-layout.js directly (using the existing SponsorReportsLayout or
equivalent wrapper) instead of the local renderSwitch Switch copy, or remove
this redundant spec if sponsor-reports-layout.test.js already covers the
drilldown-vs-list ordering.

In `@src/reducers/sponsors/__tests__/sponsor-reports-reducers.test.js`:
- Around line 110-124: Add a test for the PURCHASE_DETAILS_VALIDATION_CLEAR
branch in purchaseDetailsReducer to cover the reset path for validationError and
loading state. Use the existing PURCHASE_DETAILS_VALIDATION_ERROR test in
sponsor-reports-reducers.test.js as a guide, but assert that dispatching
PURCHASE_DETAILS_VALIDATION_CLEAR clears validationError without replacing the
current data/body so the inline/toast reset flow stays protected.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 878ce767-9e7a-43b8-97cf-abd302c54c9b

📥 Commits

Reviewing files that changed from the base of the PR and between d3f018b and 07c3261.

📒 Files selected for processing (57)
  • .env.example
  • src/actions/__tests__/sponsor-reports-actions.test.js
  • src/actions/sponsor-reports-actions.js
  • src/app.js
  • src/components/menu/menu-definition.js
  • src/components/sponsors/reports/ExportCsvButton.js
  • src/components/sponsors/reports/FilterBar.js
  • src/components/sponsors/reports/GroupByComponentView.js
  • src/components/sponsors/reports/GroupBySponsorView.js
  • src/components/sponsors/reports/GroupByToggle.js
  • src/components/sponsors/reports/OrdersTable.js
  • src/components/sponsors/reports/ReportShell.js
  • src/components/sponsors/reports/StatusPill.js
  • src/components/sponsors/reports/StatusRollupChips.js
  • src/components/sponsors/reports/SummaryPanel.js
  • src/components/sponsors/reports/TierBadge.js
  • src/components/sponsors/reports/__tests__/ExportCsvButton.test.js
  • src/components/sponsors/reports/__tests__/FilterBar.test.js
  • src/components/sponsors/reports/__tests__/GroupByComponentView.test.js
  • src/components/sponsors/reports/__tests__/GroupBySponsorView.test.js
  • src/components/sponsors/reports/__tests__/GroupByToggle.test.js
  • src/components/sponsors/reports/__tests__/OrdersTable.test.js
  • src/components/sponsors/reports/__tests__/ReportShell.test.js
  • src/components/sponsors/reports/__tests__/StatusPill.test.js
  • src/components/sponsors/reports/__tests__/StatusRollupChips.test.js
  • src/components/sponsors/reports/__tests__/SummaryPanel.test.js
  • src/components/sponsors/reports/__tests__/TierBadge.test.js
  • src/components/sponsors/reports/__tests__/statusTone.test.js
  • src/components/sponsors/reports/__tests__/usePrint.test.js
  • src/components/sponsors/reports/report-print.css
  • src/components/sponsors/reports/statusTone.js
  • src/components/sponsors/reports/usePrint.js
  • src/i18n/en.json
  • src/layouts/__tests__/sponsor-reports-layout.test.js
  • src/layouts/sponsor-layout.js
  • src/layouts/sponsor-reports-layout.js
  • src/pages/sponsors/sponsor-reports/__tests__/routing.test.js
  • src/pages/sponsors/sponsor-reports/purchase-details-report-page/__tests__/index.test.js
  • src/pages/sponsors/sponsor-reports/purchase-details-report-page/index.js
  • src/pages/sponsors/sponsor-reports/reports-landing-page/__tests__/index.test.js
  • src/pages/sponsors/sponsor-reports/reports-landing-page/index.js
  • src/pages/sponsors/sponsor-reports/sponsor-asset-drilldown-page/__tests__/index.test.js
  • src/pages/sponsors/sponsor-reports/sponsor-asset-drilldown-page/index.js
  • src/pages/sponsors/sponsor-reports/sponsor-asset-report-page/__tests__/index.test.js
  • src/pages/sponsors/sponsor-reports/sponsor-asset-report-page/index.js
  • src/reducers/sponsors/__tests__/sponsor-reports-reducers.test.js
  • src/reducers/sponsors/sponsor-reports-drilldown-reducer.js
  • src/reducers/sponsors/sponsor-reports-purchase-details-reducer.js
  • src/reducers/sponsors/sponsor-reports-sponsor-asset-reducer.js
  • src/store.js
  • src/utils/constants.js
  • src/utils/report-errors.js
  • src/utils/report-query.js
  • src/utils/reports-api.js
  • src/utils/reports-money.js
  • src/utils/reports-text.js
  • src/utils/section-csv-query.js

Comment thread src/actions/sponsor-reports-actions.js
Comment thread src/pages/sponsors/sponsor-reports/reports-money.js Outdated
- guard all report thunks against a missing currentSummit (prevents a stuck
  spinner from base(currentSummit.id) throwing after startLoading)
- formatUsd: treat blank/whitespace strings + non-finite numbers as missing
  (em dash) instead of $0.00; add reports-money tests
- FilterBar: re-sync draft when the committed value prop changes externally
- drop redundant routing.test.js (real-layout integration test in
  sponsor-reports-layout.test.js already covers route ordering)
- add PURCHASE_DETAILS_VALIDATION_CLEAR reducer test

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Ports the Sponsor Reports frontend (Purchase Details + Sponsor Assets, including per-sponsor drill-down) into summit-admin under Sponsors → Reports, using the existing admin-sponsors access gate and the in-context summit selection.

Changes:

  • Adds a new Sponsor Reports layout + routes (landing, purchase details, sponsor assets, drill-down) and wires it into the Sponsors menu.
  • Implements report UI building blocks (report shell, filters, grouping views, CSV export, print) plus supporting query/text/money utilities.
  • Introduces Redux actions/reducers for sponsor reports and adds extensive unit/integration test coverage + i18n strings.

Reviewed changes

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

Show a summary per file
File Description
src/utils/section-csv-query.js Builds section-scoped CSV export query while preserving unrelated query params.
src/utils/reports-text.js HTML-to-plain-text helper for report text fields.
src/utils/reports-money.js USD formatting helper for report summary/rows.
src/utils/reports-api.js Reports API base URL helper + positive-int route ID validator.
src/utils/report-query.js Translates report filter UI state into base-api-utils query params.
src/utils/report-errors.js Normalizes/classifies report API errors and provides a getRequest-shaped handler factory.
src/utils/constants.js Adds HTTP 503 constant used by report error classification.
src/utils/tests/reports-money.test.js Unit tests for USD formatting helper.
src/store.js Registers sponsor reports reducers and excludes their state from persistence.
src/reducers/sponsors/sponsor-reports-sponsor-asset-reducer.js Sponsor Assets grouped report reducer.
src/reducers/sponsors/sponsor-reports-purchase-details-reducer.js Purchase Details report reducer (incl. validation vs read-error split).
src/reducers/sponsors/sponsor-reports-drilldown-reducer.js Sponsor Assets drill-down reducer.
src/reducers/sponsors/tests/sponsor-reports-reducers.test.js Reducer unit tests covering all sponsor reports reducers.
src/pages/sponsors/sponsor-reports/sponsor-asset-report-page/index.js Sponsor Assets grouped report page (group-by, filters, pagination, CSV/print).
src/pages/sponsors/sponsor-reports/sponsor-asset-report-page/tests/index.test.js Sponsor Assets report page tests (dispatching, toggles, states, CSV props).
src/pages/sponsors/sponsor-reports/sponsor-asset-drilldown-page/index.js Per-sponsor drill-down page (section cards, content rendering, per-section CSV).
src/pages/sponsors/sponsor-reports/sponsor-asset-drilldown-page/tests/index.test.js Drill-down page tests including ContentCell behaviors.
src/pages/sponsors/sponsor-reports/reports-landing-page/index.js Reports landing page with two navigation cards.
src/pages/sponsors/sponsor-reports/reports-landing-page/tests/index.test.js Landing page tests for cards and links.
src/pages/sponsors/sponsor-reports/purchase-details-report-page/index.js Purchase Details report page (filters, sorting, pagination, CSV/print, tiles).
src/pages/sponsors/sponsor-reports/purchase-details-report-page/tests/index.test.js Purchase Details page tests including filter->query behavior and CSV dispatch.
src/layouts/sponsor-reports-layout.js Sponsor Reports sub-router + breadcrumb composition + admin gate.
src/layouts/sponsor-layout.js Adds /reports route under Sponsors layout.
src/layouts/tests/sponsor-reports-layout.test.js Layout routing + Restrict gate integration tests (landing, unauthorized, drilldown, crumbs).
src/i18n/en.json Adds menu label + Sponsor Reports page strings.
src/components/sponsors/reports/usePrint.js Hook wrapper around window.print() for reports.
src/components/sponsors/reports/TierBadge.js Tier badge chip with explicit tier color mapping.
src/components/sponsors/reports/SummaryPanel.js Shared summary tiles panel component.
src/components/sponsors/reports/statusTone.js Central status->chip-tone mapping helper.
src/components/sponsors/reports/StatusRollupChips.js Renders stable-order rollup chips for grouped Sponsor Assets cards.
src/components/sponsors/reports/StatusPill.js Single status chip component with shared tone mapping.
src/components/sponsors/reports/ReportShell.js Shared report page shell (header, action slot, filter slot, print CSS hook).
src/components/sponsors/reports/report-print.css Print-only CSS to print report body.
src/components/sponsors/reports/OrdersTable.js Purchase Details table built on uicore MuiTable + date/time formatting helpers.
src/components/sponsors/reports/GroupByToggle.js Toggle for sponsor/component grouping mode.
src/components/sponsors/reports/GroupBySponsorView.js Sponsor-grouped card list with drill-down links.
src/components/sponsors/reports/GroupByComponentView.js Component-grouped card list with per-sponsor links and content previews.
src/components/sponsors/reports/FilterBar.js Shared report filter bar (sponsor multiselect + optional search + extra controls).
src/components/sponsors/reports/ExportCsvButton.js CSV export button integrating uicore getCSV and access token retrieval.
src/components/sponsors/reports/tests/usePrint.test.js Unit test for print hook.
src/components/sponsors/reports/tests/TierBadge.test.js Unit tests for TierBadge rendering behavior.
src/components/sponsors/reports/tests/SummaryPanel.test.js Unit tests for SummaryPanel rendering and empty behavior.
src/components/sponsors/reports/tests/statusTone.test.js Unit tests for status tone mapping helper.
src/components/sponsors/reports/tests/StatusRollupChips.test.js Unit tests for rollup chips ordering and null rollup behavior.
src/components/sponsors/reports/tests/StatusPill.test.js Unit tests for StatusPill label behavior and tone mapping re-export.
src/components/sponsors/reports/tests/ReportShell.test.js Unit tests for ReportShell slots and icon rendering.
src/components/sponsors/reports/tests/OrdersTable.test.js Unit tests for table helpers and basic rendering/sort interactions.
src/components/sponsors/reports/tests/GroupByToggle.test.js Unit tests for group toggle behavior (ignore null toggle).
src/components/sponsors/reports/tests/GroupBySponsorView.test.js Unit tests for sponsor-grouped card rendering and link targets.
src/components/sponsors/reports/tests/GroupByComponentView.test.js Unit tests for component-grouped rendering, content hints, and links.
src/components/sponsors/reports/tests/FilterBar.test.js Unit tests for FilterBar sponsorIds emission and optional search box.
src/components/sponsors/reports/tests/ExportCsvButton.test.js Unit tests for CSV dispatch args, disabling, and in-flight click guard.
src/components/menu/menu-definition.js Adds Sponsors → Reports menu entry guarded by admin-sponsors.
src/app.js Injects SPONSOR_REPORTS_API_URL into window config.
src/actions/sponsor-reports-actions.js Thunks for reports endpoints (purchase details, sponsor assets, filters, drilldown).
src/actions/tests/sponsor-reports-actions.test.js Unit tests for sponsor reports thunks and read-error handler behavior.
.env.example Adds SPONSOR_REPORTS_API_URL example env var.

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

Comment thread src/utils/reports-text.js Outdated
Comment thread src/utils/report-errors.js
Comment thread src/utils/report-query.js Outdated
- reports-text: decode uppercase/mixed-case HTML entities (the case-insensitive
  match was missing the lowercase ENTITIES map); add toPlainText tests
- report-errors: export-disabled now dispatches { kind, status, message } to
  match the other error branches
- report-query: coerce sponsorIds to positive integers and drop the rest so the
  filter never emits sponsor_id==NaN/==0; add buildReportQuery tests

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@smarcet smarcet requested a review from santipalenque June 25, 2026 14:03
…iew)

Per Sebastian's PR review: summit-admin now requests the new microservice's own
scopes (sponsor-reports/read, sponsor-reports/export) instead of relying on the
broad reports/all. Adds SPONSOR_REPORTS_SCOPES and wires it into SCOPES
(reports/all kept for the legacy summit-reports-api). Requires the IDP to grant
these two scopes to the summit-admin client.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 @.env.example:
- Line 26: The SCOPES aggregate still includes reports/all, which is broader
than needed and conflicts with the dedicated sponsor report scopes. Update the
SCOPES value in .env.example to remove reports/all while keeping the existing
scoped report permissions such as SPONSOR_REPORTS_SCOPES and the other named
scope variables intact.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 05a2e27c-7d13-497e-bd72-9787506508fa

📥 Commits

Reviewing files that changed from the base of the PR and between 07c3261 and d342f26.

📒 Files selected for processing (12)
  • .env.example
  • src/actions/__tests__/sponsor-reports-actions.test.js
  • src/actions/sponsor-reports-actions.js
  • src/components/sponsors/reports/FilterBar.js
  • src/reducers/sponsors/__tests__/sponsor-reports-reducers.test.js
  • src/utils/__tests__/report-query.test.js
  • src/utils/__tests__/reports-money.test.js
  • src/utils/__tests__/reports-text.test.js
  • src/utils/report-errors.js
  • src/utils/report-query.js
  • src/utils/reports-money.js
  • src/utils/reports-text.js
✅ Files skipped from review due to trivial changes (2)
  • src/utils/tests/reports-text.test.js
  • src/utils/tests/reports-money.test.js
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/utils/reports-money.js
  • src/utils/report-query.js
  • src/utils/report-errors.js
  • src/reducers/sponsors/tests/sponsor-reports-reducers.test.js
  • src/actions/tests/sponsor-reports-actions.test.js

Comment thread .env.example
caseylocker and others added 7 commits June 25, 2026 12:53
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…istration

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ifest

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tails page

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@smarcet smarcet left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Two low-severity findings from the deep review — both are code-quality nits with no functional impact on the feature. @caseylocker tagging you for awareness.

Comment thread src/utils/section-csv-query.js Outdated
// Defense-in-depth: callers pass route/backend integer ids (the drill-down page
// validates :sponsorId/:summitId before rendering). Never interpolate a
// non-integer value into a filter clause sent to the CSV endpoint.
if (Number.isInteger(sid)) kept.push(`sponsor_id==${sid}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[LOW] Defense-in-depth guard does not match its comment.

Number.isInteger(0) is true, so a sponsorId of "0" would emit sponsor_id==0 into the filter clause. The drilldown page validates upstream with isPositiveIntId, so there is no live attack path — but the guard here does not live up to the comment above it ("Never interpolate a non-integer value").

Suggestion: change to sid > 0 (also handles NaN since NaN > 0 is false), and same for pid on the next line.

@caseylocker

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done in e06b1e9e. Reworded the comment to match the guard — it now describes the sponsor_id/page_id integer check and drops the stale :summitId reference (this function doesn't touch summit).

// MuiTable keys rows on row.id; the API exposes purchase_id, not id.
// The page must call rows.map(r => ({ ...r, id: r.purchase_id })) before
// passing data, or use this helper for explicit mapping.
export const getOrderRowId = (row) => row.purchase_id;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[LOW] Dead export with a misleading comment.

The comment on lines 43–45 says "The page must call rows.map(r => ({ ...r, id: r.purchase_id })) before passing data, or use this helper" — but OrdersTable already does that remapping internally in the data prop passed to MuiTable. getOrderRowId is never imported anywhere in the codebase.

A future developer reading this comment might add a redundant .map() in the page component, producing double-mapped rows.

Suggestion: either remove the export entirely, or update the comment to clarify the remapping is already done inside OrdersTable.

@caseylocker

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done in e06b1e9e. Confirmed unused — the table maps purchase_id → id inline at the data= prop; only its own test referenced it. Removed the export, comment, and test.

caseylocker and others added 2 commits June 25, 2026 16:32
Add SponsorAvatar: logo-first with a colored-initials fallback so a no-logo
(or white-on-white) sponsor is never an invisible blank. Styled to mimic the
ReportShell title icon — single primary.light rounded square with white
foreground — and used in both group-by views and the drill-down header. Switch
the ReportShell title icon glyph to white to match. Also wrap the component-view
asset filename (was truncated by noWrap+maxWidth) so the full name is readable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Render the Export CSV button on the Line Items (per-line) view of the
Purchase Details report, wired to the new backend
GET .../purchase-details/lines/csv endpoint. One ExportCsvButton switched
by view; lines query strips only pagination (preserves filters incl. the
derived include_cancelled). Orders export unchanged. Label stays 'Export CSV';
filename purchase-details-lines-summit-<id>.csv.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/pages/sponsors/sponsor-reports/purchase-details-report-page/index.js (1)

196-208: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: fold the duplicated CSV url/query derivation into a small helper.

linesCsvUrl/linesCsvQuery repeat the exact base-URL construction and page/per_page stripping already done for the orders export just above. A tiny helper (e.g. buildCsv(path, srcQuery) returning { url, query }) would remove the copy and keep both exports in sync if the strip rules change.

🤖 Prompt for 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.

In `@src/pages/sponsors/sponsor-reports/purchase-details-report-page/index.js`
around lines 196 - 208, The CSV export setup for purchase details duplicates the
same URL construction and pagination-stripping logic already used for the orders
export. Extract that repeated logic into a small shared helper in this page (for
example, a function that builds the CSV url and query from a path and source
query), then use it for both exports so `linesCsvUrl` and `linesCsvQuery` stay
aligned with the existing order export behavior.
🤖 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.

Nitpick comments:
In `@src/pages/sponsors/sponsor-reports/purchase-details-report-page/index.js`:
- Around line 196-208: The CSV export setup for purchase details duplicates the
same URL construction and pagination-stripping logic already used for the orders
export. Extract that repeated logic into a small shared helper in this page (for
example, a function that builds the CSV url and query from a path and source
query), then use it for both exports so `linesCsvUrl` and `linesCsvQuery` stay
aligned with the existing order export behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a24726cb-2af3-4f33-a505-8cb87decc70c

📥 Commits

Reviewing files that changed from the base of the PR and between 645fe0c and 809356f.

📒 Files selected for processing (2)
  • src/pages/sponsors/sponsor-reports/purchase-details-report-page/__tests__/index.test.js
  • src/pages/sponsors/sponsor-reports/purchase-details-report-page/index.js

@santipalenque santipalenque left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey @caseylocker, this PR doesn't follow our codebase's coding patterns. For example, you added several files to the utils dir that are specific to these reports. No other page or section of the admin does this — if it did, utils would end up with dozens of one-off files, defeating the purpose of having a generic utils dir.

I left a few examples inline. Could you go through the PR and align the solution with summit-admin's coding patterns?

Comment thread src/utils/reports-api.js Outdated
@@ -0,0 +1,7 @@
export const getReportsApiBaseUrl = () => window.SPONSOR_REPORTS_API_URL;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

having one utils file per page is not scalable, please remove this unnecessary helper method and move isPositiveIntId to utils/method.js file since it is a generic helper not tied to reports api

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — moved isPositiveIntId to utils/methods.js (fd1296c0) and deleted reports-api.js, repointing the import (6757a71d). No per-page utils file remains.


// Formats a DOLLAR amount (number or numeric string) as "$1,234.56".
// Non-numeric / null → em dash.
export const formatUsd = (value) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

we already have a currency parser in uicore, please use that and remove this file

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — switched to uicore currencyAmountFromCents and deleted reports-money.js (a18a6d5c). Since the uicore parser is cents-based, I also moved the reports JSON API to a cents contract (it was returning dollar strings): fntechgit/sponsor-reports-api#29. The CSV export stays in dollars for readability.

Two notes: that backend PR must deploy before/with this one (the FE now feeds these values straight to currencyAmountFromCents), and money renders without a thousands separator ($1536.00) to match the rest of summit-admin.

Comment thread src/utils/reports-text.js Outdated
"&nbsp;": " "
};

export const toPlainText = (html) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

we already have a method that does this in uicore, please use that and remove this file

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — deleted reports-text.js (500cab97). I checked uicore first: neither utils/methods nor anywhere else in the package exports an html→text helper, and summit-admin's existing htmlToString uses documentElement.textContent, which fuses adjacent-tag text (<p>a</p><b>b</b>ab). So I added a generic htmlToPlainText to methods.js (fd1296c0) — tags→space + DOMParser entity decode — and pointed both consumers at it. If you did have a specific uicore helper in mind, point me at it and I'll switch.

Comment thread src/utils/section-csv-query.js Outdated
// stripping the sponsor clause and re-emitting `status==Paid` as its own bracket turns
// OR into AND. The v1 query builder never emits mixed brackets (sponsor is always its
// own bracket), so this is a defensive edge, not a live path.
export const buildSectionCsvQuery = (

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

this is only used in reports action file, lets move it there and remove this file

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — folded into the exportSponsorAssetSectionCsv action thunk and deleted the file (306ce261).

setBusy(true);
try {
const token = await getAccessTokenSafely();
dispatch(getCSV(url, { ...query, access_token: token }, filename));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

create a method in actions file the builds the query based on page, id , filters props like every other export method in summit_admin. We need to keep a pattern in our code ir order to make it easier to understand it

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — all four CSV exports are now action thunks that own url/params/filename like exportEventRsvpsCSV: exportPurchaseDetailsCsv/exportPurchaseDetailsLinesCsv (a0310a18) and exportSponsorAssetCsv/exportSponsorAssetSectionCsv (306ce261); the pages dispatch them from a plain MUI button and ExportCsvButton.js is gone.

Two intentional consequences worth flagging: (1) the plain buttons drop the old in-flight double-click guard — that matches every other export in summit-admin (none debounce), so I left it off rather than re-add a bespoke wrapper; (2) the thunks put access_token first ({ access_token, ...query }) to match the sibling fetch thunks in the same file, so the serialized query-string order differs from the old button — param values are identical and the backend is order-independent.

caseylocker and others added 11 commits June 27, 2026 14:24
…ethods.js

Generic helpers move to the shared methods.js home (convention). htmlToPlainText
preserves tag-boundary whitespace + decodes entities (htmlToString fuses words).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…se query builders

Move report-query under the feature folder (feature helper convention). Add
buildPurchaseQuery/buildPurchaseLinesQuery so the on-screen fetch and the upcoming
export thunks share one date/order normalization (no duplication, no regression).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…text util

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move orders/lines CSV URL+params+filename building into exportPurchaseDetailsCsv/
exportPurchaseDetailsLinesCsv (cf. exportEventRsvpsCSV); page dispatches them from
a plain MUI Button. Orders CSV keeps the on-screen sort. base() now reads
window.SPONSOR_REPORTS_API_URL directly (drops getReportsApiBaseUrl import).
currentSummit removed from component props/mapStateToProps (no longer needed
after CSV URL building moved to the thunks).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…; drop ExportCsvButton

Add exportSponsorAssetCsv/exportSponsorAssetSectionCsv (section logic folded in,
simplified); pages dispatch from plain buttons. Remove the ported ExportCsvButton
and section-csv-query util.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tId from methods

- sponsor-asset-report-page/index.js: repoint isPositiveIntId import to utils/methods
- sponsor-asset-drilldown-page/index.js: merge htmlToPlainText + isPositiveIntId into one
  import from utils/methods (fixes import/no-duplicates)
- sponsor-asset-report-page/__tests__: remove jest.mock("reports-api") — pure fn, no mock needed
- sponsor-asset-drilldown-page/__tests__: same
- layouts/__tests__/sponsor-reports-layout.test.js: remove obsolete reports-api mock + comment
- purchase-details-report-page/__tests__: rename stale "ExportCsvButton" test label to
  "export button" (authorized micro-cleanup; component deleted in Task 5)
- git rm src/utils/reports-api.js — no longer consumed by any source or test
- 115 suites / 966 tests passing; 0 new lint errors

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t of shared utils

Both files moved into src/pages/sponsors/sponsor-reports/ per §2 (feature helpers
never in shared utils/); test for reports-money co-located alongside. Internal
./constants import in report-errors updated to ../../../utils/constants.
formatUsd logic unchanged — uicore/cents contract deferred to PR-B.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rors.js

./constants was not re-staged after the path update; this corrects it to
../../../utils/constants (resolves from the new feature folder location).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…998)

Keep the branch current with the master tip per smarcet's request (Core Team Sync 2026-06-26). Picks up 350c73d (#998 speaker-list activity count); clean auto-merge, en.json edits are in non-overlapping regions. No sponsor-reports files affected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Backend now emits money fields as integer cents. Drop the bespoke
reports-money.js formatter (per santi PR #997 review) and use the
platform-wide currencyAmountFromCents from openstack-uicore-foundation.
Null guard via inline/local const rather than a new shared helper file.
Test fixtures updated to cents input; assertions to no-comma "$X.XX" output.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codex follow-up: the reducer passes summary payload through unchanged, so the
dollar-string fixtures were not a live bug, but they're stale under the cents
contract. Switch total_paid fixtures to integer cents to stay representative.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@caseylocker

Copy link
Copy Markdown
Author

Hey @caseylocker, this PR doesn't follow our codebase's coding patterns. For example, you added several files to the utils dir that are specific to these reports. No other page or section of the admin does this — if it did, utils would end up with dozens of one-off files, defeating the purpose of having a generic utils dir.

I left a few examples inline. Could you go through the PR and align the solution with summit-admin's coding patterns?

@santipalenque Thank you very much for the excellent review. These are great callouts and will help me to align closer with code patterns in the project going forward. I believe that I've addressed all of your concerns with the latest push. Let me know if I missed something, especially with this one #997 (comment).

@santipalenque santipalenque left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey @caseylocker, thanks for the fixes. It looks like only the inline-commented spots were addressed — could you go through the rest of the PR and check for similar coding pattern issues elsewhere, not just the ones I flagged?

Comment thread src/actions/sponsor-reports-actions.js Outdated
const { currentSummit } = currentSummitState;
// No summit in context → skip. Otherwise base(currentSummit.id) throws
// synchronously after startLoading() and the spinner is never cleared.
if (!currentSummit?.id) return undefined;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

this function can return either undefined synchronously or a Promise depending on the branch, which is inconsistent for callers using .then()/await.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch — normalized every thunk's guard branch to return Promise.resolve() so callers always get a thenable (all 10 in this file), 35974ee.

@@ -0,0 +1,16 @@
/* src/components/sponsors/reports/report-print.css */

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

lets use inline styles following MUI standards:
`<Box
sx={{
"@media print": {
display: "none"
}
}}

`

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I looked at this one and I don't think an element-scoped sx can replace it here. This stylesheet does whole-page print isolation — body * { visibility: hidden } then .report-body, .report-body * { visibility: visible } — so on print everything except the report body is hidden. An sx={{ "@media print": { display: "none" } }} block only targets its own element + descendants, so it can't hide the rest of the page chrome the way the body * rule does. The MUI-idiomatic equivalent would be with the same @media print rules, but that's a lateral move (and there are standalone CSS/Less files elsewhere in summit-admin), so I left the stylesheet as-is. Happy to switch it to if you'd prefer that over a .css file — your call.

// eslint-disable-next-line no-magic-numbers
const PER_PAGE_OPTIONS = [10, 25, 50, 100];
// eslint-disable-next-line no-magic-numbers
const DEFAULT_PER_PAGE = 50;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

this default already exists in constants.js . Also you can build the options like this:
const BASE_PER_PAGE_OPTIONS = [DEFAULT_PER_PAGE, TWENTY_PER_PAGE, FIFTY_PER_PAGE];

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — now imports the named per-page constants from src/utils/constants.js (DEFAULT_PER_PAGE/TWENTY_PER_PAGE/FIFTY_PER_PAGE/MAX_PER_PAGE), dropped the magic numbers and the eslint-disable, 473c625.

import StatusPill from "./StatusPill";

const ISO_DATE_LENGTH = 10; // "YYYY-MM-DD"
const MS_PER_SECOND = 1000;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

this constant already exists

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — replaced with MILLISECONDS_IN_SECOND from constants, 473c625.

// (pending ClickUp 86bagnfmn). Parses date/time directly off the ISO string
// parts so the displayed time always matches the stored UTC value and tests
// stay timezone-stable regardless of the machine's local TZ offset.
export const formatCheckoutTime = (value) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

can't we use moment js to parse the date ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — formatCheckoutTime now uses moment.utc(...) instead of the hand-rolled regex, kept UTC so output is unchanged, 78bc8c6.

];

// eslint-disable-next-line no-magic-numbers
const DEFAULT_PER_PAGE = 10;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

already exists

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — removed the local DEFAULT_PER_PAGE, imports it from constants (the page already did), 473c625.

// A status token rendered as a colored, filled chip. `label` overrides the
// displayed text (e.g. a T.translate'd label); the color always derives from
// the raw `status` token via the shared tone map.
const StatusPill = ({ status, label, size = "small" }) => (

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

make this configurable so you can use it with StatusRollupChips so SatusTone can be part of this component and doesn't need to be its own file.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — folded statusTone (map + lookup) into StatusPill as a named export and deleted the standalone file; StatusRollupChips now imports statusTone from StatusPill, 6dcd257.

// src/components/sponsors/reports/__tests__/statusTone.test.js
import { statusTone } from "../statusTone";

describe("statusTone", () => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

this looks like a silly test, this file is just an array map

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Removed with the file in 6dcd257; kept the non-trivial branch coverage (case-insensitivity, unknown→default, null-safety) in StatusPill.test.js.

>
<SummaryPanel tiles={tiles} />
{/* 412 → inline toast; body preserved (rows stay visible) */}
<Snackbar

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

we already have a snackbar hook to show success or error messages

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — replaced the inline Snackbar/Alert with the existing useSnackbarMessage hook (global provider already mounted), preserving the redux validation→clear lifecycle, 7632cbe.

caseylocker and others added 12 commits June 29, 2026 10:18
- OrdersTable: replace local MS_PER_SECOND (1000) with MILLISECONDS_IN_SECOND
  and local DEFAULT_PER_PAGE (10) with the shared constant; remove eslint-disable
- LinesManifestView: replace magic-number PER_PAGE_OPTIONS [10,25,50,100] and
  DEFAULT_PER_PAGE (50) with named constants [DEFAULT_PER_PAGE, TWENTY_PER_PAGE,
  FIFTY_PER_PAGE, MAX_PER_PAGE]; default perPage prop is now FIFTY_PER_PAGE

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1oFJHczgo13Z6MnUUHjwV
…nches

Every thunk in sponsor-reports-actions.js that guards against a missing
summit returned `undefined` synchronously while the normal code-path
returned a Promise — inconsistent for callers using `.then()`/`await`.
Changed all ten guard-branch returns to `return Promise.resolve()` so
every thunk is uniformly thenable regardless of which branch is taken.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1oFJHczgo13Z6MnUUHjwV
Moves TONE_BY_STATUS + statusTone() from the standalone statusTone.js
into StatusPill.js as a named export. Updates StatusRollupChips.js to
import from StatusPill. Deletes statusTone.js and its test file; all
branch-coverage assertions migrated into StatusPill.test.js.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1oFJHczgo13Z6MnUUHjwV
Swaps the manual regex + AM/PM math in formatCheckoutTime for
moment.unix().utc() / moment.utc() + .format("YYYY-MM-DD h:mm A").
Output is byte-identical for all tested inputs. Removes dead NOON
constant and unused MILLISECONDS_IN_SECOND import; keeps ISO_DATE_LENGTH
(still needed for the date-only fallback path, and the no-magic-numbers
rule requires it named).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1oFJHczgo13Z6MnUUHjwV
…dation toast

Replaces the hand-rolled inline MUI <Snackbar>/<Alert> with the existing
useSnackbarMessage hook (openstack-uicore-foundation), consistent with
payment-view.js and mui-qr-badge-popup.js. Removes TOAST_AUTO_HIDE_MS and
the Snackbar import; Alert import kept (still used for readError). Redux
clearPurchaseDetailsValidation lifecycle preserved via useEffect.

Tests: mocks useSnackbarMessage, asserts errorMessage is called with the
correct message (and i18n fallback), clearValidation is dispatched, and
errorMessage is not called when validationError is null.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1oFJHczgo13Z6MnUUHjwV
The Purchase Details page now calls useSnackbarMessage() (T5); the layout
integration test renders that page without the global snackbar provider, so
the hook destructured undefined and threw on render. Mock it like the page's
own test does.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1oFJHczgo13Z6MnUUHjwV
bucketLinesBySponsor had exactly one production caller
(LinesManifestView). Moving the helper and its test out of
src/utils/ and co-locating them with the feature removes the
one-off-file-in-shared-utils anti-pattern flagged in PR review.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1oFJHczgo13Z6MnUUHjwV
…t-param-last

Edit A: replace local LINES_DEFAULT_PAGE_SIZE = 50 with imported FIFTY_PER_PAGE
from utils/constants; Edit B: remove `filters = {}` default on
exportPurchaseDetailsCsv — all callers pass an explicit object, buildPurchaseQuery
already defaults its own first arg.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1oFJHczgo13Z6MnUUHjwV
Replace hand-rolled new Date / setUTCDate with
moment.utc(ymd, "YYYY-MM-DD", true).add(1, "day") for consistency
with the rest of the date-formatting in this feature. Output is
byte-identical; ISO_DATE_LENGTH removed (now unused). Invalid-input
guard returns ymd unchanged rather than throwing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1oFJHczgo13Z6MnUUHjwV
Add tests for non-Z offset inputs (-05:00, +05:00) and malformed ISO-
like strings to lock current moment.utc() behavior. Backend emits UTC Z
only; offset path is inert in production — assertions are documentation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1oFJHczgo13Z6MnUUHjwV
…NT_PAGE

Replace local IMAGE_EXT regex in sponsor-asset-drilldown-page with the
repo-canonical isImageUrl from utils/methods (same extensions, also handles
query strings). Replace local FIRST_PAGE = 1 in sponsor-asset-report-page
with DEFAULT_CURRENT_PAGE from utils/constants (7 references). Replace
bare useState(1) / setPage(1) page-init/reset literals in purchase-details-
report-page with DEFAULT_CURRENT_PAGE (9 references); orderDir useState(1)
(sort direction) is intentionally unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1oFJHczgo13Z6MnUUHjwV
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.

4 participants