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

153 lines
6.8 KiB
Markdown

# 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):
```elixir
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):
```heex
<%= 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):
```elixir
authenticated: !!(conn.assigns[:current_scope] && conn.assigns.current_scope.user)
```
---
## 4. What would need to change to add a link to the editor dashboard in the main nav?
### 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:
```heex
<%= 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?
### Recommendation: Link the existing dashboard in the nav
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.