Project submission was all-or-nothing. If users left the form, they lost all progress.
A comprehensive local draft system that auto-saves form progress and allows users to resume later.
dongle/services/draft/draft.service.ts- localStorage-based draft management
- Auto-save with debouncing (1 second)
- CRUD operations for drafts
- Content detection to avoid saving empty forms
dongle/hooks/useDraft.ts- React hook wrapping the draft service
- Manages draft state and lifecycle
- Generates consistent draft IDs
- Provides simple API for components
dongle/components/projects/DraftIndicator.tsx- Visual indicator showing draft status
- Human-readable "last saved" timestamps
- Discard draft button
dongle/__tests__/services/draft.service.test.ts- Comprehensive test coverage for draft service
- Tests save, load, delete, and content detection
dongle/DRAFT_FEATURE.md- Complete feature documentation
- Usage examples
- Storage schema
Changes:
- Added draft hook integration
- Auto-saves form data on changes using
watch() - Loads existing drafts on mount
- Shows "draft restored" notification
- Added DraftIndicator component
- Clears draft after successful submission
- Added discard draft confirmation dialog
New imports:
import { useDraft } from "@/hooks/useDraft";
import { DraftIndicator } from "@/components/projects/DraftIndicator";New state:
const draft = useDraft({ mode, projectId, autoSave: true });
const [draftRestored, setDraftRestored] = React.useState(false);
const [discardDialogOpen, setDiscardDialogOpen] = useState(false);Auto-save effect:
useEffect(() => {
const subscription = watch((formData) => {
draft.saveDraft(formData as ProjectFormValues);
});
return () => subscription.unsubscribe();
}, [watch, draft]);- Debounced by 1 second to avoid excessive writes
- Only saves when form has meaningful content
- Works for both create and edit modes
- Stored in localStorage
- Survives browser refreshes and session restarts
- Separate drafts for create vs edit mode
- Edit mode drafts are per-project
- "Draft saved" indicator with timestamp
- "Your previous draft has been restored" notification
- Human-readable time format (e.g., "2m ago", "1h ago")
- "Discard Draft" button in indicator
- Confirmation dialog prevents accidental deletion
- Draft cleared after successful on-chain submission
- Drafts never trigger on-chain transactions
- Only stored locally in browser
- No server-side storage or transmission
- Draft cleared only on explicit submit or discard
✅ In-progress submission data is preserved
- Auto-saves every 1 second when form changes
- Data persists across browser sessions
- Works for both new projects and edits
✅ Users can discard drafts
- Clear "Discard Draft" button
- Confirmation dialog for safety
- Resets form to clean state
✅ Drafts are not published or submitted on-chain until explicitly submitted
- Drafts only exist in localStorage
- No blockchain interaction during save
- Draft cleared after successful on-chain submit
- Separate "Submit Registration" action required
- User navigates to
/projects/new - User starts filling form → auto-save begins
- User navigates away (draft preserved)
- User returns → sees "draft restored" message
- User sees "Draft saved X ago" indicator
- User either:
- Completes and submits → draft cleared
- Clicks "Discard Draft" → form reset
- User navigates to
/projects/[id]/edit - Form loads with current project data
- User makes changes → auto-save begins
- Separate draft maintained for this specific project
- Same flow as above
localStorage["dongle_project_drafts"] = [
{
id: "new-project-draft" | "edit-project-{id}",
mode: "create" | "edit",
projectId?: "123",
data: {
name: string,
primaryCategory: string,
tags: string[],
description: string,
websiteUrl: string,
githubUrl: string,
logoUrl: string,
docsUrl: string
},
lastSaved: "2024-01-01T12:00:00.000Z"
}
]- Create mode:
"new-project-draft" - Edit mode:
"edit-project-{projectId}" - Ensures one draft per context
- Prevents conflicts between create/edit
- 1 second debounce on auto-save
- Prevents excessive localStorage writes
- Balances responsiveness with performance
- Timers cleared on unmount
- Requires localStorage support (all modern browsers)
- Gracefully handles localStorage errors
- SSR-safe (checks for
windowobject)
Run tests with:
npm test dongle/__tests__/services/draft.service.test.tsTests verify:
- Save/load/delete operations
- Draft retrieval by mode
- Content detection
- Multiple draft handling
- Clear all functionality
Potential improvements for future iterations:
- Server-side draft storage for cross-device sync
- Draft versioning/history
- Auto-delete drafts after 30 days
- Draft list/preview page
- Export/import drafts
- Conflict resolution for concurrent edits
- Draft encryption for sensitive data