add draft-then-publish workflow plan
All checks were successful
deploy / deploy (push) Successful in 53s
All checks were successful
deploy / deploy (push) Successful in 53s
Plan for auto-saving drafts with explicit publish. Visitors always see published version, admins see their drafts while editing. Removes need for beforeunload warnings since work is never lost. Planned to follow unified-editing-mode completion. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
0741095a84
commit
ae0a149ecd
12
PROGRESS.md
12
PROGRESS.md
@ -102,6 +102,17 @@ Extend the existing page editor (PageEditorHook + editor_sheet) to include theme
|
||||
|---|---|---|---|
|
||||
| 1 | Fix double radio button dots in theme editor | 30m | done |
|
||||
|
||||
### Draft-then-publish workflow ([plan](docs/plans/draft-publish-workflow.md))
|
||||
|
||||
Auto-save drafts, explicit publish. No more lost work or `beforeunload` warnings. Visitors always see published version.
|
||||
|
||||
| Phase | Description | Depends on | Est | Status |
|
||||
|-------|-------------|------------|-----|--------|
|
||||
| 1 | Page drafts (auto-save, publish/discard) | unified-editing-mode | 5h | planned |
|
||||
| 2 | Theme drafts | Phase 1 | 4h | planned |
|
||||
| 3 | Settings drafts | Phase 2 | 4h | planned |
|
||||
| 4 | Polish (age indicator, conflict handling) | Phase 3 | 5h | planned |
|
||||
|
||||
### Platform site
|
||||
|
||||
| # | Task | Depends on | Est | Status |
|
||||
@ -150,3 +161,4 @@ All plans in [docs/plans/](docs/plans/). Completed plans are kept as architectur
|
||||
| [unified-editing-mode.md](docs/plans/unified-editing-mode.md) | Planned |
|
||||
| [profit-aware-pricing.md](docs/plans/profit-aware-pricing.md) | Planned |
|
||||
| [security-hardening.md](docs/plans/security-hardening.md) | Planned |
|
||||
| [draft-publish-workflow.md](docs/plans/draft-publish-workflow.md) | Planned |
|
||||
|
||||
187
docs/plans/draft-publish-workflow.md
Normal file
187
docs/plans/draft-publish-workflow.md
Normal file
@ -0,0 +1,187 @@
|
||||
# Draft-then-publish workflow
|
||||
|
||||
**Status:** Planned (after unified-editing-mode)
|
||||
|
||||
## Problem
|
||||
|
||||
Current editing workflow uses explicit save — changes are either saved (immediately live) or lost on navigation. This creates:
|
||||
|
||||
1. **Data loss anxiety** — navigate away accidentally, lose all work
|
||||
2. **`beforeunload` warnings** — browser-native dialogs with no custom options
|
||||
3. **No experimentation** — can't safely try changes without affecting live site
|
||||
|
||||
## Solution
|
||||
|
||||
Implement a draft-then-publish model where all edits auto-save as drafts, visitors see only published versions, and explicit "Publish" commits changes live.
|
||||
|
||||
## Design principles
|
||||
|
||||
1. **Drafts are per-user** — each admin sees their own pending changes
|
||||
2. **Visitors always see published** — no accidental exposure of drafts
|
||||
3. **Auto-save constantly** — no risk of lost work
|
||||
4. **Clear visual distinction** — obvious when viewing draft vs published
|
||||
5. **Single publish action** — one button publishes all pending changes
|
||||
|
||||
## Scope
|
||||
|
||||
Three types of editable content:
|
||||
|
||||
| Type | Storage | Draft approach |
|
||||
|------|---------|----------------|
|
||||
| Page blocks | `pages.blocks` (JSON) | `draft_blocks` column |
|
||||
| Theme settings | `settings.theme_settings` (JSON) | `draft_theme_settings` key |
|
||||
| Shop settings | `settings` table (various keys) | `draft_*` keys or `settings_drafts` table |
|
||||
|
||||
## Data model
|
||||
|
||||
### Option A: Draft columns (recommended for simplicity)
|
||||
|
||||
```elixir
|
||||
# Migration: add draft columns to pages
|
||||
alter table(:pages) do
|
||||
add :draft_blocks, :map
|
||||
add :draft_user_id, references(:users, type: :binary_id, on_delete: :nilify_all)
|
||||
add :draft_updated_at, :utc_datetime_usec
|
||||
end
|
||||
|
||||
# New settings keys
|
||||
# - "draft_theme_settings" → JSON blob
|
||||
# - "draft_site_name" → string
|
||||
# - "draft_header_nav" → JSON
|
||||
# etc.
|
||||
```
|
||||
|
||||
### Option B: Separate drafts table (more flexible)
|
||||
|
||||
```elixir
|
||||
create table(:drafts, primary_key: false) do
|
||||
add :id, :binary_id, primary_key: true
|
||||
add :user_id, references(:users, type: :binary_id, on_delete: :delete_all), null: false
|
||||
add :entity_type, :string, null: false # "page", "theme", "settings"
|
||||
add :entity_id, :string # page id, or nil for global
|
||||
add :data, :map, null: false
|
||||
timestamps(type: :utc_datetime_usec)
|
||||
end
|
||||
|
||||
create unique_index(:drafts, [:user_id, :entity_type, :entity_id])
|
||||
```
|
||||
|
||||
**Recommendation:** Start with Option A (simpler), migrate to Option B if multi-user or more complex draft needs emerge.
|
||||
|
||||
## Implementation
|
||||
|
||||
### Phase 1: Page drafts
|
||||
|
||||
| Task | Est | Notes |
|
||||
|------|-----|-------|
|
||||
| Add draft columns to pages table | 30m | Migration |
|
||||
| Update Pages context with draft functions | 1h | `save_draft/2`, `publish_draft/1`, `discard_draft/1`, `has_draft?/1` |
|
||||
| Modify PageEditorHook to auto-save drafts | 1h | Debounced save on every change |
|
||||
| Add "Publish" and "Discard" buttons to editor | 1h | Replace current "Save" |
|
||||
| Show draft indicator when viewing page with unpublished changes | 1h | Banner or badge |
|
||||
| Remove `beforeunload` warning (no longer needed) | 15m | Drafts persist |
|
||||
|
||||
### Phase 2: Theme drafts
|
||||
|
||||
| Task | Est | Notes |
|
||||
|------|-----|-------|
|
||||
| Add draft_theme_settings to settings | 30m | New key |
|
||||
| Update Settings context with theme draft functions | 1h | Similar to pages |
|
||||
| Modify theme editor to auto-save drafts | 1h | |
|
||||
| Theme preview shows draft, shop shows published | 1h | Conditional CSS injection |
|
||||
| Add publish/discard to theme editor | 30m | |
|
||||
|
||||
### Phase 3: Settings drafts
|
||||
|
||||
| Task | Est | Notes |
|
||||
|------|-----|-------|
|
||||
| Decide which settings are draftable | 30m | Not all need drafts (e.g. API keys) |
|
||||
| Add draft handling to Settings context | 1.5h | |
|
||||
| Update settings editor to use drafts | 1h | |
|
||||
| Unified "Publish all" option | 1h | Publish page + theme + settings together |
|
||||
|
||||
### Phase 4: Polish
|
||||
|
||||
| Task | Est | Notes |
|
||||
|------|-----|-------|
|
||||
| Draft age indicator ("Last saved 5 mins ago") | 30m | |
|
||||
| Draft conflict handling (stale draft warning) | 1h | Edge case: published changed since draft created |
|
||||
| Version history / rollback (stretch goal) | 3h | Store previous published versions |
|
||||
|
||||
## UX flow
|
||||
|
||||
### Editing a page
|
||||
|
||||
1. Admin navigates to page, clicks "Edit"
|
||||
2. Editor panel opens, showing current published state
|
||||
3. Admin makes changes — **auto-saved as draft every few seconds**
|
||||
4. Admin can navigate away freely — draft persists
|
||||
5. Admin returns later — draft loaded automatically
|
||||
6. "Publish" button commits draft → published
|
||||
7. "Discard" button deletes draft, reverts to published
|
||||
|
||||
### Visual indicators
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ ⚠️ You have unpublished changes (last saved 2 mins ago) │
|
||||
│ [Publish] [Discard] │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
When admin views a page with a draft:
|
||||
- Banner at top of editor panel
|
||||
- "Unpublished" badge next to page title
|
||||
- Different background tint in editor (subtle)
|
||||
|
||||
### Visitors see published only
|
||||
|
||||
The `render_page/1` function checks:
|
||||
1. If admin is viewing AND has draft → show draft
|
||||
2. Otherwise → show published `blocks`
|
||||
|
||||
```elixir
|
||||
defp get_blocks_for_render(page, current_user) do
|
||||
if current_user && page.draft_user_id == current_user.id && page.draft_blocks do
|
||||
page.draft_blocks
|
||||
else
|
||||
page.blocks
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Migration path
|
||||
|
||||
1. Deploy with draft support disabled (feature flag)
|
||||
2. Run migration to add draft columns
|
||||
3. Enable draft mode
|
||||
4. Existing pages continue working (no draft = show published)
|
||||
5. New edits create drafts
|
||||
|
||||
## Future considerations
|
||||
|
||||
- **Multi-user drafts** — currently single draft per page, could extend to per-user drafts
|
||||
- **Draft preview link** — shareable URL that shows draft to non-admins (for review)
|
||||
- **Scheduled publishing** — "Publish at 9am tomorrow"
|
||||
- **Draft comparison** — side-by-side diff of draft vs published
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Unified editing mode (phases 1-7) must be complete first
|
||||
- This work builds on the editor panel UI established there
|
||||
|
||||
## Estimates
|
||||
|
||||
| Phase | Est |
|
||||
|-------|-----|
|
||||
| Phase 1: Page drafts | 5h |
|
||||
| Phase 2: Theme drafts | 4h |
|
||||
| Phase 3: Settings drafts | 4h |
|
||||
| Phase 4: Polish | 5h |
|
||||
| **Total** | **18h** |
|
||||
|
||||
## References
|
||||
|
||||
- [Squarespace draft model](https://support.squarespace.com/hc/en-us/articles/205815578-Saving-and-publishing-site-changes)
|
||||
- [Shopify theme versions](https://shopify.dev/docs/themes/architecture#theme-versions)
|
||||
- [WordPress autosave](https://developer.wordpress.org/plugins/post/autosave/)
|
||||
Loading…
Reference in New Issue
Block a user