From ae0a149ecdc779b7ddbaba7f5e77e354b0a34550 Mon Sep 17 00:00:00 2001 From: jamey Date: Mon, 9 Mar 2026 11:07:46 +0000 Subject: [PATCH] add draft-then-publish workflow plan 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 --- PROGRESS.md | 12 ++ docs/plans/draft-publish-workflow.md | 187 +++++++++++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 docs/plans/draft-publish-workflow.md diff --git a/PROGRESS.md b/PROGRESS.md index 474f787..feb9d23 100644 --- a/PROGRESS.md +++ b/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 | diff --git a/docs/plans/draft-publish-workflow.md b/docs/plans/draft-publish-workflow.md new file mode 100644 index 0000000..f4159f4 --- /dev/null +++ b/docs/plans/draft-publish-workflow.md @@ -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/)