Skip to content

Excel export is an HTML table renamed .xls, and it blocks the main thread for up to 5000 rows #54

Description

@royalpinto007

Problem

exportSavedProspectsToExcel in src/app/(site)/prospects/savedProspectsExport.ts does not produce an Excel file. It builds an HTML <table> string and downloads it as:

new Blob([html], { type: "application/vnd.ms-excel;charset=utf-8" }),
"signalizeai_saved_prospects.xls",

That is a 2005 era trick. Current Excel opens it behind a "the file format and extension do not match, this file could be corrupted" security warning. Numbers and Google Sheets handle it inconsistently, .xls is blocked outright by some corporate mail gateways, and every cell is a string, so the readiness score cannot be sorted numerically and the saved date is not a date.

There is a second problem in the same path. fetchSavedProspectsExportRows in savedProspectsData.ts requests limit=5000 in one round trip, and the export then does rows.map(flattenProspect) and builds one giant template literal, all synchronously on the main thread inside a click handler. For a user near that limit the tab freezes with no feedback, because there is no progress state and the work is not yielded.

Why it matters

Export is the moment a user moves their data into the tool they actually sell from. A scary format warning at that moment undermines the whole product, and a frozen tab reads as a crash.

Suggested approach

  1. Emit a real .xlsx. The sibling extension repo already depends on exceljs for exactly this, so using it here keeps the two exports consistent. Set real cell types so scores are numbers and savedAt is a date, set column widths, and freeze the header row.
  2. Coordinate with CSV and Excel export is vulnerable to spreadsheet formula injection #40, which is about formula injection in the exported cells. A real writer makes that fix cleaner, because you can set the cell type explicitly instead of prefixing strings.
  3. Make the export async and yield: show a progress or busy state on the export button, and chunk the row mapping so the main thread is not blocked for thousands of rows.
  4. Decide what happens above 5000 rows. Right now the cap is silent. Either paginate through all rows or tell the user their export was truncated.

Done when

  • The downloaded file opens in Excel, Numbers, and Google Sheets with no format warning.
  • Score and date columns have the correct cell types.
  • Exporting a large account shows progress and does not freeze the tab.
  • Hitting the row cap is communicated rather than silent.

If you want to take this on, comment on the issue to claim it and it will be assigned. Please keep to a maximum of 2 open claims per person at a time so other contributors get a chance.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesthelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions