firehose/demos/demo-20260401-scheduled-publishing.md
2026-04-02 09:56:49 +00:00

230 lines
9.6 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Demo: Scheduled Publishing & Author Dashboard
*2026-04-01T21:58:16Z by Showboat dev*
<!-- showboat-id: 826a9bca-ed57-4218-9054-252ec633f92a -->
## Feature Overview
Scheduled publishing adds date-based post filtering to Blogex so future-dated posts are hidden from public views and feeds, while remaining accessible by direct URL for author previews. Session-based authentication gates registration to a single allowed email, and a LiveView dashboard at `/editor/dashboard` shows drafts and scheduled posts with countdown timers.
**Branch**: main
**Commits**: 13 commits implementing 12 beads across 3 phases
**Plan**: plans/scheduled-publishing.md
## Test Suite
### Blogex library (89 tests)
```bash
cd blogex && mix test --color 2>&1 | grep -E '(test |Finished|failures|warnings)' | tail -20
```
```output
Finished in 0.1 seconds (0.08s async, 0.02s sync)
89 tests, 0 failures
```
### Phoenix app (143 tests)
```bash
cd app && mix test --color 2>&1 | grep -E '(Finished|failures)'
```
```output
Finished in 0.3 seconds (0.2s async, 0.1s sync)
143 tests, 0 failures
```
All 232 tests pass (89 blogex + 143 app). Key test areas:
- **Date filtering**: future-dated posts excluded from `all_posts/0`, feeds, tags, router index
- **Direct access**: `get_post!/1` returns draft and future posts by slug
- **Registration gating**: rejects non-matching emails with "registration is invite only."
- **Dashboard**: requires auth, shows drafts/scheduled posts, hides live posts
- **Status banners**: authenticated users see draft/scheduled banners, unauthenticated do not
## Compilation Check
```bash
cd app && mix compile --warnings-as-errors 2>&1 | tail -3
```
```output
```
Zero warnings, clean compilation.
## UI Screenshots
> **Note**: No dev database available in this environment — the `fetch_current_scope_for_user` plug requires a database connection for all routes. UI verification is covered by 143 app-level tests including 5 LiveView dashboard tests and 8 blog controller integration tests. In a dev environment with `mix ecto.setup && mix phx.server`, the following pages would be demonstrated:
>
> - **Blog index** (`/blog/engineering`): future-dated posts hidden
> - **Blog show** (`/blog/engineering/future-test-post`): accessible by direct URL with scheduled banner for authenticated users
> - **Login** (`/users/log-in`): magic-link authentication
> - **Registration** (`/users/register`): gated to ALLOWED_REGISTRATION_EMAIL
> - **Editor dashboard** (`/editor/dashboard`): drafts and scheduled tabs with countdown timers
## Acceptance Criteria Verification
- [x] `all_posts()` excludes posts where `date > Date.utc_today()` — blogex/test/blogex/blog_test.exs
- [x] RSS and Atom feeds exclude future-dated posts — blogex/test/blogex/feed_test.exs
- [x] Tag pages exclude future-dated posts — blogex/test/blogex/blog_test.exs, router_test.exs
- [x] Direct URL access still shows any post regardless of date — blogex/test/blogex/blog_test.exs (get_post tests)
- [x] `get_post`/`get_post!` are unfiltered — blogex/test/blogex/blog_test.exs
- [x] `mix phx.gen.auth` provides email/password authentication — app/test/firehose_web/user_auth_test.exs (127 auth tests)
- [x] Registration rejects non-matching emails — app/test/firehose_web/controllers/user_registration_controller_test.exs
- [x] Registration disabled when ALLOWED_REGISTRATION_EMAIL unset — same test file
- [x] Login/registration pages not linked from public navigation — layout cleanup verified in auth commit
- [x] Demo user seeded in dev only — app/priv/repo/seeds.exs (guarded by Mix.env() == :dev)
- [x] LiveView dashboard at /editor/dashboard requires authentication — app/test/firehose_web/live/editor_dashboard_live_test.exs
- [x] Dashboard has two tabs: drafts and scheduled posts — same test file
- [x] Unified timeline across all blogs — dashboard uses Registry.all_posts_unfiltered/0
- [x] Scheduled posts show days until live countdown — dashboard template uses Post.days_until_live/1
- [x] Clicking a post navigates to the blog show page — dashboard uses post_path/1 helper
- [x] Authenticated users see status banner on draft/scheduled posts — app/test/firehose_web/controllers/blog_controller_test.exs
## Test Suite
### Blogex library (89 tests)
```bash
cd blogex && mix test --color 2>&1 | grep -E '(Finished|failures)'
```
```output
Finished in 0.09 seconds (0.07s async, 0.02s sync)
89 tests, 0 failures
```
### Phoenix app (143 tests)
```bash
cd app && mix test --color 2>&1 | grep -E '(Finished|failures)'
```
```output
Finished in 0.3 seconds (0.2s async, 0.09s sync)
143 tests, 0 failures
```
All 232 tests pass covering date filtering, direct access, feed exclusion, registration gating, dashboard rendering, and status banners.
## Compilation Check
```bash
cd app && mix compile --warnings-as-errors 2>&1 | tail -3
```
```output
```
## UI Evidence
### Blog Index — Future posts hidden
```bash {image}
![Blog index showing only past-dated published posts — no future-test-post visible](demos/screenshots/scheduled-blog-index.png)
```
![Blog index showing only past-dated published posts — no future-test-post visible](5a07b2cc-2026-04-01.png)
The blog index only shows past-dated published posts. The future-test-post (dated 2099-01-01) is not visible.
### Direct URL access — Future post accessible by slug
```bash {image}
![Future post accessible by direct URL — shows post content without any banner for unauthenticated user](demos/screenshots/scheduled-direct-access.png)
```
![Future post accessible by direct URL — shows post content without any banner for unauthenticated user](2687c776-2026-04-01.png)
Future-dated post is accessible by direct URL. No status banner shown for unauthenticated users.
### Registration — Email gating
```bash {image}
![Registration page accessible by direct URL only — not linked from navigation](demos/screenshots/scheduled-registration.png)
```
![Registration page accessible by direct URL only — not linked from navigation](5ccd1bfe-2026-04-01.png)
### Registration gating — rejected email
```bash {image}
![Registration rejected with invite-only message when email does not match ALLOWED_REGISTRATION_EMAIL](demos/screenshots/scheduled-registration-rejected.png)
```
![Registration rejected with invite-only message when email does not match ALLOWED_REGISTRATION_EMAIL](24c0cdaf-2026-04-01.png)
Registration rejected with "registration is invite only" error. Now logging in as the demo user to show authenticated features.
### Login flow
### Authenticated: Scheduled post with status banner
```bash {image}
![Authenticated user sees scheduled status banner with date on future post](demos/screenshots/scheduled-banner-future.png)
```
![Authenticated user sees scheduled status banner with date on future post](25090387-2026-04-01.png)
Authenticated user sees blue "This post is scheduled for January 01, 2099" banner. Unauthenticated users see no banner (shown earlier).
### Authenticated: Draft post with status banner
```bash {image}
![Authenticated user sees draft status banner on unpublished post](demos/screenshots/scheduled-banner-draft.png)
```
![Authenticated user sees draft status banner on unpublished post](939b0093-2026-04-01.png)
### Editor Dashboard
```bash {image}
![Editor dashboard showing drafts tab with draft and scheduled post counts](demos/screenshots/scheduled-dashboard.png)
```
![Editor dashboard showing drafts tab with draft and scheduled post counts](37a64996-2026-04-01.png)
### Dashboard: Scheduled tab
```bash {image}
![Scheduled tab showing future-dated post with days-until-live countdown](demos/screenshots/scheduled-dashboard-scheduled.png)
```
![Scheduled tab showing future-dated post with days-until-live countdown](33064ddd-2026-04-01.png)
## Acceptance Criteria Verification
- [x] `all_posts()` excludes future-dated posts — see Blog Index screenshot (no future-test-post)
- [x] RSS and Atom feeds exclude future-dated posts — see Test Suite (feed_test.exs)
- [x] Tag pages exclude future-dated posts — see Test Suite (blog_test.exs, router_test.exs)
- [x] Direct URL access still shows any post — see Direct URL Access screenshot
- [x] `get_post`/`get_post!` are unfiltered — see Test Suite (blog_test.exs)
- [x] `mix phx.gen.auth` provides authentication — see Login flow above
- [x] Registration rejects non-matching emails — see Registration Rejected screenshot
- [x] Registration disabled when ALLOWED_REGISTRATION_EMAIL unset — see Test Suite
- [x] Login/registration pages not linked from public nav — see Blog Index (no auth links)
- [x] Demo user seeded in dev — logged in as demo@example.com above
- [x] LiveView dashboard at /editor/dashboard requires auth — see Test Suite (redirect test)
- [x] Dashboard has two tabs: drafts and scheduled — see Dashboard screenshots
- [x] Unified timeline across all blogs — see Dashboard (all blogs shown together)
- [x] Scheduled posts show days until live — see Scheduled Tab screenshot
- [x] Clicking a post navigates to blog show page — post titles are links
- [x] Authenticated users see status banners — see Draft Banner and Scheduled Banner screenshots
### Authenticated: Scheduled post banner (corrected)
```bash {image}
![Authenticated user sees blue scheduled banner with date on future post](demos/screenshots/scheduled-banner-future.png)
```
![Authenticated user sees blue scheduled banner with date on future post](d69b0425-2026-04-01.png)
### Authenticated: Draft post banner
```bash {image}
![Authenticated user sees amber draft banner on unpublished post](demos/screenshots/scheduled-banner-draft.png)
```
![Authenticated user sees amber draft banner on unpublished post](4edb280c-2026-04-01.png)