4.4 KiB
4.4 KiB
Admin Autosave Completion Guide
Objectives
- Eliminate redundant save requests triggered on initial page load.
- Restore reliable local draft recovery, including clear-up of stale backups.
- Deliver autosave status feedback that visibly transitions back to
idleafter successful saves. - Ensure navigation/unload flows wait for pending autosaves instead of cancelling them mid-flight.
Key Problem Areas
Missing Draft Handlers
src/routes/admin/posts/[id]/edit/+page.svelte:425referencesrestoreDraftanddismissDraft, but the functions are never defined. Draft recovery buttons therefore break compilation and runtime behavior.
Immediate Autosaves on Load
- Effects in
src/routes/admin/posts/[id]/edit/+page.svelte:307andsrc/lib/components/admin/ProjectForm.svelte:157callautoSave.schedule()as soon as the component mounts. Because the payload hash includesupdatedAt, each mount triggers redundant PUTs until the server response realigns the hash.
Ineffective Navigation Guard
beforeNavigate(() => autoSave.flush())(posts + project form) does not cancel the outbound navigation, so the flush typically aborts when the route unloads. Result: unsaved work if the user navigates away during a pending autosave.
Controller Lifecycle Gaps
createAutoSaveControllertimers/AbortController persist after leaving the page because callers never invokedestroy().- Post editor imports
clearDraftbut never clears the draft after successful saves or when dismissing the prompt, so stale backups reappear.
Controller Enhancements (src/lib/admin/autoSave.ts)
- Baseline priming: Add a
prime(initialPayload)(or allowonSavedto pass the response payload) to setlastSentHashimmediately after fetching server data. This prevents an automatic save when the user has not made changes. - Auto-idle transition: When status becomes
'saved', set a timeout (e.g., 2s) that reverts status to'idle'. Cancel the timeout on any new state change. - Robust destroy: Ensure
destroy()clears pending timers and aborts the current request; expose and require callers to invoke it on component teardown. - Consider optional helper flags (e.g.,
autoResetStatus) so forms do not reimplement timing logic.
Shared Lifecycle Helper
Create a utility (e.g., initAutoSaveLifecycle) that accepts the controller plus configuration:
- Registers keyboard shortcut (
Cmd/Ctrl+S) toflush()once the page has loaded. - Provides a real navigation guard that cancels the navigation event, awaits
flush(), then resumes or surfaces an error. - Hooks into
onDestroyto remove listeners and callcontroller.destroy(). - Optionally wires window unload handling if needed.
Form Integration Checklist
Posts Editor (src/routes/admin/posts/[id]/edit/+page.svelte)
- Implement
restoreDraft/dismissDraftand handleclearDraftafter autosave or manual save success. - Introduce a
hasLoadedflag set afterloadPost()(and controllerprime) before scheduling autosave. - Adopt the shared lifecycle helper for navigation, keyboard shortcuts, and cleanup.
Project Form (src/lib/components/admin/ProjectForm.svelte)
- Mirror baseline priming and
hasLoadedgating before scheduling. - Clear drafts on success or dismissal, and reuse the lifecycle helper.
- Ensure autosave only starts after the initial project data populates
formData.
Other Forms (Simple Post, Essay, Photo, etc.)
- Audit each admin form to ensure they use the shared lifecycle helper, seed baselines, clear drafts, and transition status back to
idle.
Testing & Verification
- Unit Tests: Cover controller state transitions, baseline priming, abort handling, and auto-idle timeout (
tests/autoSaveController.test.ts). Run withnode --test --loader tsx tests/autoSaveController.test.ts. - Component Tests: Verify autosave does not fire on initial mount, drafts restore/clear correctly, and navigation waits for flush.
- Manual QA: Confirm keyboard shortcut behavior, offline fallback, and that UI returns to
idleafter showing “saved”.
Structural Considerations
- Factor shared autosave wiring into reusable modules to avoid copy/paste drift.
- Ensure server response payloads used in
prime()reflect the canonical representation (including normalized fields) so hashes stay in sync. - Document the lifecycle helper so new admin screens adopt the proven pattern without regression.