feat(statuses): admin-configurable custom statuses + dynamic roadmap …#1589
Open
hodyhq wants to merge 7 commits into
Open
feat(statuses): admin-configurable custom statuses + dynamic roadmap …#1589hodyhq wants to merge 7 commits into
hodyhq wants to merge 7 commits into
Conversation
…(Implements feedback.fider.io/posts/111)
Introduces a tenant-scoped statuses table that supersedes the
hardcoded enum.PostStatus list. Admins manage their own catalogue
from /admin/statuses — rename, recolor, set the semantic kind, toggle
visibility on the home / filter / roadmap, reorder, or add brand-new
slugs (e.g. "Under Review", "Triage", "Beta Testing").
Schema
- statuses (tenant_id, slug, label, kind, color, icon, show_on_home,
show_on_roadmap, filterable, sort_order, is_system, is_active,
legacy_enum used only for backfill, created_at, updated_at)
- statuses.kind ∈ {open, active, closed-completed, closed-declined, duplicate}
- posts.status_slug — new authoritative identifier
- Migrations seed the six built-ins (open, planned, started,
completed, declined, duplicate) for every existing tenant, backfill
posts.status_slug from posts.status, then DROP posts.status +
statuses.legacy_enum. New tenants auto-seed via SeedTenantStatuses
on signup.
- 202606241500 adds show_on_roadmap with a kind-based backfill so
Planned/Started/Completed lanes appear on /roadmap unchanged.
Backend
- entity.Post carries StatusSlug + StatusKind (joined from the
statuses table). enum.PostStatus is no longer a field. JSON keeps
"status" emitting the slug so existing clients still parse it.
- buildPostQuery LEFT JOINs statuses ps on (tenant_id, slug) to
expose ps.kind. Default home view filters statuses.kind IN
(open, active) — admin-added active statuses surface automatically.
- CanBeVoted now branches on kind NOT IN
(closed-completed, closed-declined, duplicate); markPostAsDuplicate
writes status_slug = 'duplicate'.
- Status-change webhook emits post_status_slug, post_status_kind,
post_status_label, post_old_status_slug, post_old_status_label.
Slug-aware short-circuit guard handles custom slugs sharing the
default int enum.
Roadmap
- /roadmap lanes derived from tenant.statuses where is_active and
show_on_roadmap. Ordered by sort_order. Horizontal scroll when
there are more than fit (each column min-width 320px).
Admin UI
- /admin/statuses lists the catalogue with Up/Down sort-order
arrows, toggle-active, Edit (label/color/icon/sort_order/visibility),
Delete (refused for system rows and for statuses still attached to
posts).
Backwards compatibility
- Built-in slugs (open, planned, started, completed, declined,
duplicate) keep their names so existing webhook receivers reading
post_status_slug see the same values they would have read from
post_status before.
- Migrations are additive then drop the legacy int column; rollback
is via DB restore.
Closes feedback.fider.io/posts/111
- actions/post_test.go: SetResponse.Status is now a slug string, swap enum.PostDeleted for "deleted". - tasks/delete_post_test.go: post.Status is gone; webhook payload emits post_status_slug now — update both assertions and the fixture's StatusSlug. - ESLint --fix on LF line endings across the PR set (CRLF leaked in from the Windows checkout). Drop unused _e parameter and ButtonClickEvent import on ManageStatuses.page.tsx.
- seed status catalogue in setup.sql so postgres tests see the new LEFT JOIN ps.kind path return rows - register no-op SeedTenantStatuses handler in CreateTenant tests - register GetStatusBySlug handler in SetResponse tests - defer recover() in attachTenantStatuses middleware so unit tests with a bus that lacks the handler still pass (errors here are already non-fatal per the docstring) - set StatusSlug on the global feed test fixture so i18n lookup resolves instead of returning a Missing-Translation warning - restore upstream VoteCounter layout (count inside button)
The migration 202606231500 drops legacy_enum from statuses, so seeding it in the test fixture fails with 'column does not exist'.
Vote handlers gate via Post.CanBeVoted(), which reads StatusKind. SetPostResponse only mutated StatusSlug on the in-memory Post, so callers that reused the struct (e.g. AddVote right after marking a post completed) saw the stale empty kind and skipped the closed check, letting votes through on closed posts.
'deleted' is the internal tombstone slug and has no statuses row by design. Treat the not-found result from the kind lookup as empty kind rather than failing the whole command.
The empty-state check excluded closed-completed columns, so a roadmap with only Completed posts rendered as 'waiting for its first update'. With custom statuses, show_on_roadmap is the explicit opt-in; any column flagged on should keep the board visible.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
…(Implements feedback.fider.io/posts/111)
Introduces a tenant-scoped statuses table that supersedes the hardcoded enum.PostStatus list. Admins manage their own catalogue from /admin/statuses — rename, recolor, set the semantic kind, toggle visibility on the home / filter / roadmap, reorder, or add brand-new slugs (e.g. "Under Review", "Triage", "Beta Testing").
Schema
Backend
Roadmap
Admin UI
Backwards compatibility
Closes feedback.fider.io/posts/111