Nothing guards a draft: one click in the sidebar throws away an unsent cover letter #258

Closed
opened 2026-09-17 15:07:06 +00:00 by tiagoagueda · 0 comments
Owner

There is no beforeunload handler anywhere in app.js — 1,916 lines, none of them about
losing work — and no autosave. The cover letter body is an eighteen-row textarea
(documents/forms.py:205); a CV summary is four, an override six, notes three. Navigation
is a full page load almost everywhere, because htmx appears in seven of 182 templates. So
the sidebar, the search box, the back button and every link on the page are one click away
from discarding whatever is in that textarea, silently and with no way back.

Nothing in Postulo has ever asked somebody to retype a letter. It has simply never been
tested by anybody who clicked the wrong thing an hour in.

Two fixes, and they are not the same size

The guard. Mark a form dirty on first input; ask before unloading. It is a dozen lines
and it stops the whole class of loss. Three things it must get right:

  • it must not fire on the form's own submit, which is the classic way to make this
    infuriating;
  • the wording is the browser's, not ours — no string we can translate, which is unusual
    for this project and worth saying out loud in the code rather than discovering later;
  • the capture review's keys (d discards and moves on, j skips) navigate too, and must
    respect the same dirty state or they become the fastest way to lose a correction.

The draft. Keeping what was typed, so the answer to a crash or a closed tab is not "start
again". That is a larger decision and should be made deliberately:

  • a draft in localStorage is data living outside the database, per-browser, invisible to
    export and to every other device — which cuts against how the rest of Postulo treats a
    person's data, and should be said plainly in the interface if it is done that way;
  • a draft on the server is consistent with everything else here, but needs a field or a
    table, a rule for when it is cleared, and an answer for what a half-written letter means
    when somebody looks at the list of letters;
  • either way it is restored on purpose — a banner saying what was found and offering it
    back — never silently, because silently restoring stale text over something deliberate is
    a worse failure than the one being fixed.

The guard is worth doing on its own and worth doing first. The draft can follow, or not.

There is no `beforeunload` handler anywhere in `app.js` — 1,916 lines, none of them about losing work — and no autosave. The cover letter body is an eighteen-row textarea (`documents/forms.py:205`); a CV summary is four, an override six, notes three. Navigation is a full page load almost everywhere, because htmx appears in seven of 182 templates. So the sidebar, the search box, the back button and every link on the page are one click away from discarding whatever is in that textarea, silently and with no way back. Nothing in Postulo has ever asked somebody to retype a letter. It has simply never been tested by anybody who clicked the wrong thing an hour in. ## Two fixes, and they are not the same size **The guard.** Mark a form dirty on first input; ask before unloading. It is a dozen lines and it stops the whole class of loss. Three things it must get right: - it must **not** fire on the form's own submit, which is the classic way to make this infuriating; - the wording is the **browser's**, not ours — no string we can translate, which is unusual for this project and worth saying out loud in the code rather than discovering later; - the capture review's keys (`d` discards and moves on, `j` skips) navigate too, and must respect the same dirty state or they become the fastest way to lose a correction. **The draft.** Keeping what was typed, so the answer to a crash or a closed tab is not "start again". That is a larger decision and should be made deliberately: - a draft in `localStorage` is data living outside the database, per-browser, invisible to export and to every other device — which cuts against how the rest of Postulo treats a person's data, and should be said plainly in the interface if it is done that way; - a draft on the server is consistent with everything else here, but needs a field or a table, a rule for when it is cleared, and an answer for what a half-written letter means when somebody looks at the list of letters; - either way it is restored **on purpose** — a banner saying what was found and offering it back — never silently, because silently restoring stale text over something deliberate is a worse failure than the one being fixed. The guard is worth doing on its own and worth doing first. The draft can follow, or not.
tiagoagueda added this to the 0.4.0 milestone 2026-09-17 15:07:06 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Postulo/postulo#258
No description provided.