firehose/investigation/question-1-drafts-scheduled-list.md
Firehose Bot fe156ef81e Add Dashboard nav link for authenticated users
- Add conditional "Dashboard" link in navbar that shows when a user is logged in
- Tests verify the link is hidden for unauthenticated users and visible when authenticated
- Investigation reports for Q1 (drafts/scheduled), Q2 (RSS), Q3 (email subs)
2026-07-06 09:11:13 +01:00

6.8 KiB

Question 1: "Is there a way to see all drafts and scheduled posts in a list?"

Answer Summary

Yes — an editor dashboard exists at /editor/dashboard showing all drafts and scheduled posts, but it is not linked from the navigation. Only users who know the URL can access it.

Public routes (index, homepage, tag pages) never show drafts/scheduled in production. However, authenticated users can view individual draft/scheduled posts by direct URL if they know the slug — the show template detects visibility and shows a status banner.


1. Existing Views/Routes for Drafts & Scheduled Posts

Editor Dashboard (LiveView) — /editor/dashboard

  • File: app/lib/firehose_web/live/editor_dashboard_live.ex
  • Route: live "/editor/dashboard", EditorDashboardLive (router.ex line 87)
  • Auth: Requires authenticated user (:require_authenticated_user pipe, line 84)
  • Data source: Blogex.Registry.all_posts_unfiltered() — returns ALL posts regardless of published/date
  • Features:
    • Two tabs: Drafts (published=false) and Scheduled (future date)
    • Count badges per tab
    • Shows title, author, date, status badge, and days-until-live for scheduled posts
    • Links each post to its public URL

BlogController.show — individual draft/scheduled post viewing

  • File: app/lib/firehose_web/controllers/blog_controller.ex
  • Route: GET /blog/:blog_id/:slug (router.ex line 38)
  • Auth: No auth middleware — but show template only shows status banners when authenticated
  • Data source: blog.get_post!() (line 22) uses unfiltered_posts() — returns ANY post by slug, including drafts/scheduled
  • Show template (app/lib/firehose_web/controllers/blog_html/show.html.heex, lines 6-22):
    • Shows a draft banner (amber) only when @authenticated and visibility is :draft
    • Shows a scheduled banner (blue) only when @authenticated and visibility is :scheduled

BlogController.index / tag — do NOT show drafts/scheduled

  • Uses blog.paginate() / blog.posts_by_tag() which call all_posts() — filtered in production
  • See section 3 for details

2. Is the Editor Dashboard linked from the main navigation?

No. The navbar in app/lib/firehose_web/components/layouts/app.html.heex (lines 1-23) contains:

Link Target
Blog /blog/engineering
Releases /blog/releases
QWAN https://qwan.eu (external)
Contact /contact
Theme toggle inline JS

There are no user/authentication-related links at all — no login, logout, settings, or dashboard link. The layout does not reference @current_scope or any authenticated-user assigns.


3. Do any public routes show draft or scheduled posts?

Route Uses Shows drafts/scheduled?
GET / (homepage) recent_posts(3)all_posts() No (filtered)
GET /blog/:blog_id (index) paginate()all_posts() No (filtered)
GET /blog/:blog_id/tag/:tag posts_by_tag()all_posts() No (filtered)
GET /blog/:blog_id/:slug (show) get_post!()unfiltered_posts() Yes — any authenticated user can view via direct URL, with status banner

Filtering logic (in Blogex.Blog, app/lib/firehose/blog/blog.ex lines 85-90):

def all_posts do
  today = Date.utc_today()
  if Blogex.show_drafts?() do
    Enum.filter(@posts, &(not Date.after?(&1.date, today)))
  else
    Enum.filter(@posts, &(&1.published and not Date.after?(&1.date, today)))
  end
end
  • Dev mode (config/config.exs line 78): show_drafts: true — includes drafts (published=false) but excludes future-dated (scheduled)
  • Prod mode (config/prod.exs line 17): show_drafts: false — only live posts visible

Important gap: all_posts() in dev includes drafts but NOT scheduled. The editor dashboard (all_posts_unfiltered()) handles both.

Show template control flow (show.html.heex lines 4-22):

<%= if @authenticated and @visibility == :draft do %>
  <div>Draft — not published</div>
<% end %>

<%= if @authenticated and @visibility == :scheduled do %>
  <div>This post is scheduled for ...</div>
<% end %>

The @authenticated flag comes from conn.assigns[:current_scope] (BlogController line 33):

authenticated: !!(conn.assigns[:current_scope] && conn.assigns.current_scope.user)

Single-file change: app/lib/firehose_web/components/layouts/app.html.heex

The app layout receives @current_scope from the fetch_current_scope_for_user plug in the browser pipeline (router.ex line 16). Currently it doesn't use it.

Required addition: A conditional link inside the <ul class="flex flex-row ..."> that shows only when @current_scope has a user:

<%= if @current_scope && @current_scope.user do %>
  <li>
    <a href="/editor/dashboard" class="btn btn-ghost btn-sm">Dashboard</a>
  </li>
<% end %>

No router changes needed — the route and LiveView already exist with proper authentication guards.

Optionally, also add login/logout links for unauthenticated users, so they know how to reach the dashboard.


5. What would be the simplest way to make drafts/scheduled visible to editors?

The EditorDashboardLive at /editor/dashboard is fully functional — it shows all drafts and scheduled posts with proper sorting and status badges. The only missing piece is discoverability.

Simplest change:

  1. Add a conditional "Dashboard" link to app.html.heex (as described in #4 above) — ~3 lines of code
  2. Optionally add login/logout links so users can authenticate

Why this is sufficient:

  • The dashboard is already built and tested
  • It respects authentication (only shows for logged-in users)
  • It covers both drafts and scheduled (unlike dev-mode all_posts() which only covers drafts)
  • Posts link to their public URLs for preview

Alternative considered: Add drafts/scheduled to public blog index

This would be more invasive — it would require:

  • Changing BlogController.index to check for authentication and use unfiltered_posts() when authenticated
  • Adding an auth plug to the blog routes (currently no auth pipeline there)
  • Modifying templates to show status badges

This approach would leak draft titles to unauthenticated users if not done carefully, and is more complex than simply linking the existing dashboard.

Live post preview (already exists)

Authenticated users can already view individual draft/scheduled posts by direct URL (/blog/engineering/some-draft-slug) with a status banner — BlogController.show uses get_post!() which returns unfiltered posts. The show template checks @authenticated to display the draft/scheduled banner.