title: Verify feeds exclude future-dated posts

description:
## Context
RSS/Atom feeds call blog.all_posts() which should now filter by date (from Step 1).
Add explicit tests confirming feeds exclude future-dated published posts.

## Scope
- blogex/test/blogex/feed_test.exs

## TDD
RED: Test RSS and Atom feeds exclude future-dated published posts
GREEN: Should already pass from Step 1 changes
REFACTOR: None

status: closed

issue_type: task

created_by: Willem van den Ende

close_reason: Closed

type: blocks


title: Make get_post/get_post! search all compiled posts (unfiltered)

description:
## Context
get_post/1 and get_post!/1 currently search all_posts() (filtered). Change to search @posts (unfiltered)
so direct URL access works for draft and scheduled posts. Enables preview links for reviewers.

## Scope
- blogex/lib/blogex/blog.ex: get_post/1, get_post!/1
- blogex/test/support/fake_blog.ex: get_post/1, get_post!/1
- blogex/test/blogex/blog_test.exs: update existing tests, add new ones

## TDD
RED: Test get_post! returns future-dated post, get_post returns draft post
GREEN: Search @posts instead of all_posts()
REFACTOR: Update existing test that expects get_post!("draft-post") to raise


title: Add date filtering to Blogex all_posts/0

description:
## Context
all_posts() in blogex/lib/blogex/blog.ex (line 77-83) currently filters by `published` boolean only.
Add `date <= Date.utc_today()` filter so future-dated posts are hidden from public views.

## Scope
- blogex/lib/blogex/blog.ex: all_posts/0
- blogex/test/support/fake_blog.ex: all_posts/0
- blogex/test/blogex/blog_test.exs: new tests
- blogex/test/support/setup.ex: add future-dated post to default_posts

## TDD
RED: Test that future-dated published post is excluded from all_posts, posts_by_tag, recent_posts, all_tags
GREEN: Add date filter after published filter
REFACTOR: Extract filtering predicate if duplicated


title: Add post visibility and days_until_live helpers

description:
## Context
Dashboard and status banners need to compute post visibility (draft/scheduled/live)
and days until a scheduled post goes live.

## Scope
- blogex/lib/blogex/post.ex: add visibility/1 and days_until_live/1
- blogex/test/blogex/post_test.exs: new tests

## TDD
RED: Test visibility returns :draft/:scheduled/:live correctly, days_until_live returns integer or nil
GREEN: Implement functions
REFACTOR: None


title: Create LiveView editor dashboard

description:
## Context
LiveView at /editor/dashboard behind auth. Two tabs: drafts and scheduled.
Unified timeline across all blogs. Scheduled posts show "X days until live".
Links to post show page.

## Scope
- app/lib/firehose_web/live/editor_dashboard_live.ex
- app/lib/firehose_web/router.ex: add /editor scope
- app/test/firehose_web/live/editor_dashboard_live_test.exs

## TDD
RED: Unauth redirected, auth sees dashboard, drafts tab, scheduled tab with countdown, links work
GREEN: Implement LiveView, add route
REFACTOR: Extract tab component if markup duplicated

depends_on_id: firehose-dhh


title: Gate registration to ALLOWED_REGISTRATION_EMAIL

description:
## Context
Registration must be restricted to a single email from env var.
Unset = disabled. Wrong email = "registration is invite only."

## Scope
- app/config/runtime.exs: read ALLOWED_REGISTRATION_EMAIL
- app/config/test.exs: set test value
- Registration controller or Accounts context: add validation
- Registration tests: add gating tests

## TDD
RED: Registration succeeds for matching email, fails for non-matching, fails when unset
GREEN: Add config reading + validation check
REFACTOR: None


title: Add unfiltered post access for dashboard

description:
## Context
Dashboard needs access to all posts including drafts and future-dated.
Add unfiltered_posts/0 to Blog macro and all_posts_unfiltered/0 to Registry.

## Scope
- blogex/lib/blogex/blog.ex: add unfiltered_posts/0
- blogex/lib/blogex/registry.ex: add all_posts_unfiltered/0
- blogex/test/support/fake_blog.ex: add unfiltered_posts/0
- blogex/test/blogex/registry_test.exs: new tests

## TDD
RED: Test unfiltered returns all posts including drafts and future-dated
GREEN: Implement functions
REFACTOR: None


title: Add integration tests for scheduled post filtering in Phoenix

description:
## Context
Phoenix blog controller tests need to verify date filtering works end-to-end.
May need a far-future markdown test fixture (2099/01-01-future-post.md).

## Scope
- app/test/firehose_web/controllers/blog_test.exs
- app/priv/blog/engineering/2099/01-01-future-post.md (test fixture)

## TDD
RED: Blog index hides future post, show page returns it, tag page excludes it
GREEN: Should pass from Blogex changes
REFACTOR: None

issue_id: firehose-apw


title: Run mix phx.gen.auth and configure

description:
## Context
No auth exists. Run mix phx.gen.auth Accounts User users.
Remove auth links from public nav (login/registration are hidden URLs).

## Scope
- Generated files in app/lib/firehose/accounts/, app/lib/firehose_web/
- app/lib/firehose_web/router.ex
- Layout files (root.html.heex, app.html.heex) — remove injected auth links

## TDD
RED: Generated tests should pass
GREEN: Run generator, migrate, verify
REFACTOR: Remove auth links from public navigation


title: Seed demo user in dev

description:
## Context
Seed demo@example.com / password123 in dev environment only.
Use Accounts context from phx.gen.auth.

## Scope
- app/priv/repo/seeds.exs

## TDD
Trivial — manual verification


title: Show draft/scheduled status banners for authenticated users

description:
## Context
When authenticated user views a draft or scheduled post via direct URL,
show a banner: "Draft — not published" or "This post is scheduled for {date}".
Unauthenticated users see no banner.

## Scope
- app/lib/firehose_web/controllers/blog_controller.ex: pass visibility to template
- app/lib/firehose_web/controllers/blog_html/show.html.heex: conditional banner
- app/test/firehose_web/controllers/blog_test.exs: banner tests

## TDD
RED: Auth user sees banner on draft/scheduled, no banner on live, unauth sees no banner
GREEN: Compute visibility, pass to template, render conditionally
REFACTOR: Extract banner component if reusable


title: Verify router respects date filtering

description:
## Context
Blogex.Router index, tag, and feed routes use all_posts()/posts_by_tag() (now filtered).
The /:slug route uses get_post() (now unfiltered). Add tests confirming correct behaviour.

## Scope
- blogex/test/blogex/router_test.exs

## TDD
RED: Test GET / excludes future posts, GET /tag/:tag excludes, GET /:slug returns future post
GREEN: Should pass from Steps 1-2
REFACTOR: None

issue_id: firehose-vyw