Skip to content

Fix export column labels, redownloadable exports, and duplicate webp images - #628

Merged
kelvinkipruto merged 2 commits into
mainfrom
ft/fix-csv-downloads
Aug 10, 2026
Merged

Fix export column labels, redownloadable exports, and duplicate webp images#628
kelvinkipruto merged 2 commits into
mainfrom
ft/fix-csv-downloads

Conversation

@kelvinkipruto

Copy link
Copy Markdown
Contributor

Description

  • CSV/JSON export headers now show human-readable labels (Tenant Name, Political Entity, Title, ...) instead of raw field names (tenantName, politicalEntity, title, ...), for both the Promises and AI Extraction Export Rows collections.
  • Saved exports can now be re-downloaded at any time. Previously, once an export was saved, its Download button disappeared entirely on revisit (the Exports collection denies update access to everyone by design, and Payload only mounts that button when the viewer has update permission).
  • Fixed duplicate Media records being created on every Airtable sync for images in .webp/.gif/.avif format.

Details

Column labelssrc/lib/exportColumnLabels.ts adds an export before hook that renames each exported row's keys from raw field names to their already-configured admin label, wired into promises and ai-extraction-export-rows in src/plugins/index.ts. (Note: an export of a collection with zero matching rows still shows raw field names in the header — the hook only runs when there's data to transform, and that's not something a plugin hook can override.)

Redownloadable exports — root cause was more than "disabled once modified": the Exports collection's access.update is hardcoded to always deny, so Payload never renders the edit-view Save/Download button on a saved doc at all. Added ExportDownloadButton in the beforeDocumentControls slot, which renders unconditionally and serves the already-generated file directly. Also forked the plugin's create-time button (ExportSaveButton) so it downloads the saved file instead of regenerating when nothing's changed.

Webp dedup — Payload always re-encodes webp/gif/avif uploads through sharp before saving (even with no resize/format options configured), but the app's duplicate-detection checksum was computed from the raw downloaded bytes — two different byte streams that can never match. computeMediaChecksum (src/utils/files.ts) now detects those three formats by magic bytes and replicates Payload's exact re-encode step before hashing, so sync runs correctly recognize repeat images instead of re-uploading them every time. Applies to both createPoliticalEntity.ts and downloadDocuments.ts, which had the identical bug.

Fixes # (issue)

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Screenshots

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation

…edup

CSV/JSON exports (Promises, AI Extraction Export Rows) showed raw field
names as column headers (tenantName, politicalEntityName, ...) instead of
the labels already configured on each field. Added an export `before`
hook (src/lib/exportColumnLabels.ts) that relabels each row's keys using
the field's admin label before the file is written.

Saved exports also had no way to be re-downloaded: the plugin's Exports
collection denies update access to everyone by design (exports are
immutable once generated), so Payload never mounts the edit-view
Save/Download button on a saved doc — not merely disabled, entirely
absent once you navigate away and back. Added ExportDownloadButton in the
unconditionally-rendered `beforeDocumentControls` slot to serve the
already-generated file directly, and forked the plugin's create-time
button (ExportSaveButton) so it only regenerates when there's no saved
file yet.

Finally, fixed duplicate Media docs being created for .webp/.gif/.avif
images synced from Airtable. Payload always re-encodes those three
mimetypes through sharp before persisting them, even with no resize
options configured, so the dedup checksum computed from the raw
downloaded bytes never matched what was actually stored. computeMediaChecksum
now detects those formats by magic bytes and replicates Payload's
re-encode step before hashing, so the pre-check and stored checksums are
computed over the same bytes.
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
PromiseTracker Ready Ready Preview Aug 10, 2026 10:27am

Request Review

@kelvinkipruto

Copy link
Copy Markdown
Contributor Author

@codex Review

chatgpt-codex-connector[bot]

This comment was marked as resolved.

Addresses PR review feedback suggesting the AVIF magic-byte check should
also inspect the ISO-BMFF compatible-brands list, not just the major
brand. Verified against file-type@21.3.4 (the exact library Payload uses
internally to set `file.mimetype`) — it also switches on the major brand
alone and never reads compatible brands, so this must match it exactly to
correctly predict whether Payload will reencode a given upload. Checking
compatible brands would make this detector more accurate than file-type
itself and reintroduce the checksum mismatch this change fixes, for a
file whose major brand is a generic HEIF brand (e.g. "mif1") with "avif"
only listed as a compatible brand. No behavior change — comment only.
@kelvinkipruto
kelvinkipruto merged commit 5ccce27 into main Aug 10, 2026
9 checks passed
@kelvinkipruto
kelvinkipruto deleted the ft/fix-csv-downloads branch August 10, 2026 10:29
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