Skip to content

feat: add JSON-LD download and consolidate report download actions#146

Merged
jfrench9 merged 5 commits into
mainfrom
feature/report-serialization
May 29, 2026
Merged

feat: add JSON-LD download and consolidate report download actions#146
jfrench9 merged 5 commits into
mainfrom
feature/report-serialization

Conversation

@jfrench9
Copy link
Copy Markdown
Member

Summary

This PR introduces JSON-LD serialization support for reports and improves the download experience by consolidating multiple download buttons into a single unified dropdown menu.

Changes

Report Viewer Enhancements (src/app/(app)/reports/[id]/content.tsx)

  • New Download JSON-LD action: Users can now export reports in JSON-LD format directly from the report viewer, enabling structured data interoperability and semantic web compatibility.
  • Consolidated Download dropdown: Previously separate download buttons (e.g., PDF, and now JSON-LD) have been collapsed into a single "Download" dropdown menu, reducing toolbar clutter and providing a cleaner, more scalable UI pattern for future export formats.

Dependency Updates

  • Bumped @robosystems/client from previous version to 0.3.36, which likely includes the underlying API support for JSON-LD report serialization.

Key UI/UX Improvements

  • 🎯 Reduced visual clutter: Multiple download buttons replaced with a single dropdown, simplifying the report viewer toolbar.
  • 📦 JSON-LD export: Enables users to download report data in a standardized linked data format, useful for data integration, archival, and machine-readable workflows.
  • 🔄 Scalable pattern: The dropdown approach makes it straightforward to add additional export formats in the future without further UI changes.

Breaking Changes

None. This is an additive feature change. Existing download functionality is preserved within the new dropdown UI.

Testing Notes for Reviewers

  1. Download dropdown behavior: Navigate to a report at /reports/[id] and verify the download button renders as a dropdown with all available options (including JSON-LD).
  2. JSON-LD download: Click the "Download JSON-LD" option and verify:
    • The file downloads successfully with appropriate filename and .jsonld or .json extension.
    • The downloaded content is valid JSON-LD (check for @context, @type fields as applicable).
  3. Existing download actions: Confirm that any previously available download formats (e.g., PDF) still function correctly within the new dropdown.
  4. Edge cases: Test with reports that may have missing or incomplete data to ensure the serialization handles these gracefully.
  5. Dropdown accessibility: Verify the dropdown can be navigated via keyboard (Tab, Enter, Escape) and that aria attributes are present.

Browser Compatibility Considerations

  • The dropdown component should be tested across Chrome, Firefox, Safari, and Edge.
  • Verify that the file download (likely using Blob / URL.createObjectURL or an anchor download) works consistently across browsers, especially Safari which can have differing behavior with programmatic downloads.
  • Ensure no issues with popup blockers intercepting the download action.

🤖 Generated with Claude Code

Branch Info:

  • Source: feature/report-serialization
  • Target: main
  • Type: feature

Co-Authored-By: Claude noreply@anthropic.com

jfrench9 added 4 commits May 28, 2026 17:04
Wires the new ``LedgerClient.getReportBundleDownloadUrl`` SDK
method (0.3.35) into the saved-report viewer at /reports/[id]. The
button sits in the page-header actions row next to Share, scoped
to ``generationStatus === 'published'`` Reports — pre-publish
drafts have no stamped bundle to download.

Click flow:

1. SDK call fetches a short-lived presigned URL from the backend
   (300s lifetime).
2. ``window.location.href = downloadUrl`` triggers the browser
   download — the backend sets Content-Disposition: attachment
   with a versioned filename (``{report_id}-v{n}.jsonld``), so
   navigating to the URL writes the file to disk rather than
   rendering it inline.

Error feedback surfaces in a dismissible Alert above the status
banner, carrying the server-returned detail string (e.g. "Report
has no stamped bundle — regenerate to produce one", which is the
expected failure for Reports published before the serialization
feature shipped).

In-flight state disables the button and swaps the icon for a
spinner — the SDK round-trip is the only thing the user waits
for; the download itself starts as soon as the browser hits the
presigned URL.
The two side-by-side ``Download JSON-LD`` and ``Download XBRL``
buttons read as cluttered now that we have format alternatives.
Replace with a single ``Download`` button that opens a dropdown
listing the available formats:

  Download ▾
  ├── JSON-LD bundle
  └── XBRL 2.1 package

Cleaner toolbar; trivially extensible when new flavors arrive
(Turtle, OIM xBRL-JSON, etc.) — each becomes another ``DropdownItem``
without growing the header's button count. Disabled state on the
dropdown trigger reflects either in-flight download; per-item
disabled reflects the specific format's spinner.

XBRL handler:
* Calls the new SDK ``getReportBundleXbrlZip`` method (Blob +
  server-suggested filename + generation count from response
  headers).
* Creates an object URL, anchor-clicks it with the suggested
  filename, then schedules ``URL.revokeObjectURL`` after 5 seconds
  so the browser's download path completes before the URL is
  released (immediate revoke cancels in-flight saves on some
  browsers).

Tested end to end through puppeteer: dropdown opens cleanly, XBRL
option triggers an SDK call returning a 10KB zip with the right
Content-Type, the anchor click triggers the download with the
suggested filename. JSON-LD path remains unchanged from the prior
implementation.
Picks up ``getReportBundleDownloadUrl`` (presigned URL for the
stamped JSON-LD bundle) and ``getReportBundleXbrlZip`` (binary
stream for the XBRL 2.1 zip) from the published TypeScript SDK.
The /reports/[id] page already uses both — the dep bump replaces
the ``file:../robosystems-typescript-client`` local-dev swap with
the published version.

420 tests pass, lint clean.
@jfrench9
Copy link
Copy Markdown
Member Author

@claude please review this PR

@claude
Copy link
Copy Markdown

claude Bot commented May 29, 2026

Claude finished @jfrench9's task in 1m 35s —— View job


PR Review: feat: add JSON-LD download and consolidate report download actions

Tasks:

  • Read PR context and understand changes
  • Examine changed files in detail
  • Analyze code quality, correctness, and potential issues
  • Post review feedback

Overview

Overall this is a solid, well-structured addition. The two-strategy download pattern (presigned URL redirect for JSON-LD, blob/anchor for XBRL) is architecturally sound and the individual concerns below are mostly minor. A few things worth addressing before merge.


Issues

1. Version bump violates project convention (medium)

package.json bumps @robosystems/client from 0.3.340.3.36. The CLAUDE.md explicitly states:

Never bump versions in package.json / pyproject.toml — version bumps are owned by the user on publish.

The underlying SDK functionality may require this bump, but the version pin should be reverted and the user should own that change separately. Fix this →


2. Redundant disabled on DropdownItem (low)

content.tsx lines 342–353

<Dropdown
  disabled={isDownloadingBundle || isDownloadingXbrl}  // ← whole dropdown locked
  ...
>
  <DropdownItem
    onClick={handleDownloadBundle}
    disabled={isDownloadingBundle}   // ← dead — parent is already disabled
  >
  <DropdownItem
    onClick={handleDownloadXbrl}
    disabled={isDownloadingXbrl}     // ← dead — parent is already disabled
  >

When the parent Dropdown is disabled, individual DropdownItem disabled props are never evaluated. The per-item disabled props are dead code and add misleading noise. If the intent was to let users click the non-loading item while the other is in-flight (e.g., click XBRL while JSON-LD is downloading), then the parent disabled condition needs to change instead — but that introduces a UX inconsistency (spinner shows with an open dropdown). Current behavior is conservative and fine, just drop the per-item disabled. Fix this →


3. Comment volume exceeds project conventions (low)

content.tsx lines 93–99, 114–119, 144–148, 165

CLAUDE.md says: "Default to writing no comments. Only add one when the WHY is non-obvious... Don't explain WHAT the code does."

The added comment blocks are thorough but several explain the what rather than the why. The most defensible comments are:

  • Cross-origin behavior of window.location.href vs <a download> (line 117–119) — keep
  • The setTimeout revocation delay note (line 165) — keep

The large header docblock on handleDownloadBundle (lines 114–142) and the paragraph on the XBRL approach (lines 144–148) should be condensed to a single line each (or removed entirely if the function signature and variable names are self-explanatory).


4. PR description omits XBRL (low — documentation only)

The PR title and body describe JSON-LD as the new addition and do not mention the XBRL 2.1 download. The diff clearly adds both handleDownloadBundle and handleDownloadXbrl. The title/description should be updated before merge so reviewers and the git log reflect what actually shipped.


Things Done Well

  • Two-strategy approach is correct. window.location.href is the right choice for a cross-origin presigned URL (the download attribute on <a> elements is ignored for cross-origin URLs in all major browsers). The blob/anchor approach for the XBRL streamed response is also the right call.
  • Shared downloadError state works. Each handler calls setDownloadError(null) at the top, so stale errors don't leak across attempts.
  • 5-second revocation delay on the XBRL object URL is a good practice — immediate revocation can abort the download in some browsers.
  • generationStatus === 'published' gate on the dropdown is correct; no point offering downloads on a report still generating.
  • Error surfacing strategy is solid — surfaces the backend 404 message for stale reports rather than a generic string.

| Branch: feature/report-serialization

…ments

The parent Download dropdown is already disabled while either download is
in flight, so the per-item disabled props never evaluated. Also condense
the download-handler comments to the non-obvious why.
@jfrench9 jfrench9 merged commit 94ccd61 into main May 29, 2026
3 checks passed
@jfrench9 jfrench9 deleted the feature/report-serialization branch May 29, 2026 02:26
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.

1 participant