[Epic][Blog][UX] Blog management & UX overhaul — findings from the 2026-07-18 full review #705

Open
opened 2026-07-19 01:14:48 +00:00 by spikerj · 2 comments
Owner

Context

Joey requested a full-scale UI/UX + feature review of learn.spikersoft.com/blog (management surfaces, element positioning, enhancement opportunities). The review's headline gap — authors couldn't delete their own posts — is implemented outside this epic:

  • spikersoft-backend #422 — retention purge: 7-day window then full erasure (disk media, blogs/quarantine bucket objects, workflow docs with EXIF/GPS PII; moderation audit anonymized)
  • spikersoft-backend #423 — create-provenance hardening ([Authorize] + AuthorId from Keycloak sub)
  • spikersoft-angular #420 — the owner-delete affordance on post cards

Everything else found in the review becomes this epic's children, in rough priority order:

Children

  1. Media delete broken end-to-end [bug, high]
  2. Comment identity hardcoded [bug]
  3. "My posts" management view [feature]
  4. Author edit UI [feature]
  5. Server-side pagination + filtering [perf/UX]
  6. Responsive media grid [UX]
  7. Create-flow navigation + unsaved-changes guard [UX]
  8. Moderation shield tooltip [UX, small]
  9. Theme-token colors in blog SCSS [UX, small]
  10. Blog e2e spec [test]
  11. Unify base-URL sources [tech debt]
  12. Provenance-gap record [security, closes with backend #423]

(Each filed as its own ticket referencing this epic; numbers linked in comments below as they're filed.)

Review evidence base

Angular: libraries/features/blog/** (list/entry/create components), libraries/domain/blog/** (service — full API surface incl. unused getMyPosts), routes (/blog public, /blog/create auth, /admin/blog-moderation admin). Backend: BlogController (20 endpoints), 3 Mongo collections, 5-consumer media pipeline. Full traces in the child tickets.

## Context Joey requested a full-scale UI/UX + feature review of learn.spikersoft.com/blog (management surfaces, element positioning, enhancement opportunities). The review's headline gap — **authors couldn't delete their own posts** — is implemented outside this epic: - spikersoft-backend **#422** — retention purge: 7-day window then full erasure (disk media, `blogs`/`quarantine` bucket objects, workflow docs with EXIF/GPS PII; moderation audit anonymized) - spikersoft-backend **#423** — create-provenance hardening (`[Authorize]` + AuthorId from Keycloak `sub`) - spikersoft-angular **#420** — the owner-delete affordance on post cards Everything else found in the review becomes this epic's children, in rough priority order: ## Children 1. **Media delete broken end-to-end** [bug, high] 2. **Comment identity hardcoded** [bug] 3. **"My posts" management view** [feature] 4. **Author edit UI** [feature] 5. **Server-side pagination + filtering** [perf/UX] 6. **Responsive media grid** [UX] 7. **Create-flow navigation + unsaved-changes guard** [UX] 8. **Moderation shield tooltip** [UX, small] 9. **Theme-token colors in blog SCSS** [UX, small] 10. **Blog e2e spec** [test] 11. **Unify base-URL sources** [tech debt] 12. **Provenance-gap record** [security, closes with backend #423] (Each filed as its own ticket referencing this epic; numbers linked in comments below as they're filed.) ## Review evidence base Angular: `libraries/features/blog/**` (list/entry/create components), `libraries/domain/blog/**` (service — full API surface incl. unused `getMyPosts`), routes (`/blog` public, `/blog/create` auth, `/admin/blog-moderation` admin). Backend: `BlogController` (20 endpoints), 3 Mongo collections, 5-consumer media pipeline. Full traces in the child tickets.
Author
Owner

Children filed (priority order):

# Ticket Class
#706 Media delete broken end-to-end (placeholder handler + click-less confirm button) bug, high
#707 Comment identity hardcoded + unauthenticated comment endpoints bug
#708 "My posts" management view (getMyPosts unused) feature
#709 Author edit UI (+ update-endpoint authz hardening) feature
#710 Server-side pagination (metadata currently discarded) perf/UX
#711 Responsive media grid (hardcoded cols=3) UX
#712 Create-flow setTimeout navigation + unsaved-changes guard UX
#713 Polish batch: shield tooltip + theme tokens UX, small
#714 Blog e2e spec (testids now in place) test
#715 Dual base-URL sources tech debt
#716 Provenance-gap record (closes with backend #423) security

Implementation PRs for the headline feature (outside this epic's scope): backend #422 (retention purge), backend #423 (create provenance), angular #420 (owner-delete UI).

Children filed (priority order): | # | Ticket | Class | |---|---|---| | #706 | Media delete broken end-to-end (placeholder handler + click-less confirm button) | bug, **high** | | #707 | Comment identity hardcoded + unauthenticated comment endpoints | bug | | #708 | "My posts" management view (`getMyPosts` unused) | feature | | #709 | Author edit UI (+ update-endpoint authz hardening) | feature | | #710 | Server-side pagination (metadata currently discarded) | perf/UX | | #711 | Responsive media grid (hardcoded cols=3) | UX | | #712 | Create-flow setTimeout navigation + unsaved-changes guard | UX | | #713 | Polish batch: shield tooltip + theme tokens | UX, small | | #714 | Blog e2e spec (testids now in place) | test | | #715 | Dual base-URL sources | tech debt | | #716 | Provenance-gap record (closes with backend #423) | security | Implementation PRs for the headline feature (outside this epic's scope): backend **#422** (retention purge), backend **#423** (create provenance), angular **#420** (owner-delete UI).
Author
Owner

Epic status roll-up — audited every child against origin/master. Owner-delete shipped; none of the eight UX children have started. Posting once here rather than eight near-identical comments.

Child State Evidence
#708 "My posts" management view not started zero my-posts/myPosts hits in the blog lib or routes.ts
#709 Author edit UI not started see below — service method exists, no caller
#710 Pagination UI not started zero mat-paginator/MatPaginator in libraries/features/blog/**
#711 Media grid mobile not started blog-entry-component.html:189 still cols="3"
#712 Create-flow nav + guard not started blog-create.ts:276 and :326 still setTimeout(() => this.router.navigate(["/blog"]), 1000); no CanDeactivate anywhere
#713 Polish batch not started no shield matTooltip found
#714 Blog E2E coverage not started zero blog spec files under e2e/playwright/
#715 Two base-URL sources not started still live — and now self-documented, see below

#709 is another instance of the pattern this audit keeps hitting. libraries/domain/blog/src/lib/blog.service.ts:183 defines updateBlogPost(dto: UpdateBlogPostDto): Observable<BlogPost> — and git grep -n "updateBlogPost" origin/master -- '*.ts' returns exactly that one line. Server side and service side both exist; no surface calls it. That's the seventh confirmed case in this audit (alongside #593's ExtractChapters, #663's /quiz/{id}/review, #698's registry, #756's DLQ config, #847's Enabled flag, #785's SCSS class).

#715 has become better-documented rather than fixed, which is worth recording. libraries/features/blog/README.md:33 now names it explicitly as a "Code smell", notes it's "same shape as the domain → platform debt that drove Phase 4h's API_BASE_URL DI refactor", and prescribes the fix — add a MEDIA_BASE_URL token (or reuse API_BASE_URL) to @spikersoft/shared-api-config. So the analysis is done and written down; only the change is outstanding. The domain lib itself is clean (libraries/domain/blog/README.md:16-17 — zero boundary warnings since Phase 4h); it's BlogEntryComponent building media URLs from ${environment.baseUrl}${media.url} that remains.

#714 is the one I'd prioritise. Blog has zero Playwright coverage while this epic proposes changing the create flow (#712), the list (#710), the grid (#711) and adding an edit surface (#709). Landing those against no E2E coverage on a user-facing content surface is how #785 shipped green — its specs asserted on the component model while the template was never wired.

Epic scope check: the body describes owner-delete as delivered (backend #422/#423 + angular #420), and that's consistent with what I see. So this epic is one shipped slice plus eight untouched ones — worth deciding whether it stays a single epic or the UX batch gets re-scoped, since nothing has moved on it since 2026-07-19.

**Epic status roll-up** — audited every child against `origin/master`. **Owner-delete shipped; none of the eight UX children have started.** Posting once here rather than eight near-identical comments. | Child | State | Evidence | |---|---|---| | #708 "My posts" management view | ❌ not started | zero `my-posts`/`myPosts` hits in the blog lib or `routes.ts` | | #709 Author edit UI | ❌ not started | see below — **service method exists, no caller** | | #710 Pagination UI | ❌ not started | zero `mat-paginator`/`MatPaginator` in `libraries/features/blog/**` | | #711 Media grid mobile | ❌ not started | `blog-entry-component.html:189` still `cols="3"` | | #712 Create-flow nav + guard | ❌ not started | `blog-create.ts:276` and `:326` still `setTimeout(() => this.router.navigate(["/blog"]), 1000)`; no `CanDeactivate` anywhere | | #713 Polish batch | ❌ not started | no shield `matTooltip` found | | #714 Blog E2E coverage | ❌ not started | **zero** blog spec files under `e2e/playwright/` | | #715 Two base-URL sources | ❌ not started | still live — and now self-documented, see below | **#709 is another instance of the pattern this audit keeps hitting.** `libraries/domain/blog/src/lib/blog.service.ts:183` defines `updateBlogPost(dto: UpdateBlogPostDto): Observable<BlogPost>` — and `git grep -n "updateBlogPost" origin/master -- '*.ts'` returns **exactly that one line**. Server side and service side both exist; no surface calls it. That's the seventh confirmed case in this audit (alongside #593's `ExtractChapters`, #663's `/quiz/{id}/review`, #698's registry, #756's DLQ config, #847's `Enabled` flag, #785's SCSS class). **#715 has become better-documented rather than fixed**, which is worth recording. `libraries/features/blog/README.md:33` now names it explicitly as a **"Code smell"**, notes it's "same shape as the `domain → platform` debt that drove Phase 4h's `API_BASE_URL` DI refactor", and prescribes the fix — add a `MEDIA_BASE_URL` token (or reuse `API_BASE_URL`) to `@spikersoft/shared-api-config`. So the analysis is done and written down; only the change is outstanding. The domain lib itself is clean (`libraries/domain/blog/README.md:16-17` — zero boundary warnings since Phase 4h); it's `BlogEntryComponent` building media URLs from `${environment.baseUrl}${media.url}` that remains. **#714 is the one I'd prioritise.** Blog has **zero** Playwright coverage while this epic proposes changing the create flow (#712), the list (#710), the grid (#711) and adding an edit surface (#709). Landing those against no E2E coverage on a user-facing content surface is how #785 shipped green — its specs asserted on the component model while the template was never wired. **Epic scope check:** the body describes owner-delete as delivered (backend #422/#423 + angular #420), and that's consistent with what I see. So this epic is one shipped slice plus eight untouched ones — worth deciding whether it stays a single epic or the UX batch gets re-scoped, since nothing has moved on it since 2026-07-19.
Sign in to join this conversation.