Skip to content

feat: direct-submit UI, chat flag, licence lane on community writes - #45

Merged
Robbertvermeulen merged 7 commits into
mainfrom
feat/direct-submit-ui
Sep 20, 2026
Merged

Robbertvermeulen merged 7 commits into
mainfrom
feat/direct-submit-ui

Conversation

@Robbertvermeulen

Copy link
Copy Markdown
Contributor

Simplified Community v1, SDK side. Built and reviewed via the SDD run of 2026-09-19/20 (task reviews, scoped re-reviews, opus whole-branch review, plus Robbert's live browser round).

  • Consumer::email() (breaking interface addition), Config::enableChat (default false): with chat off, no chat UI renders and the chat-only REST proxy routes are not registered; feed goes full-width.
  • "New topic" opens the review dialog in an empty direct mode (Question/Bug/Feature request, title, free text, moderation notice); POST <ns>/topics proxy → hub /cases/direct; 402 maps to the licence-invalid kind.
  • Pending topics: disabled vote arrows, composer visible with disabled submit + notice; no vote/comment fetches (no 403 noise).
  • Licence + email now travel on comment create and vote cast (deletes/retracts unchanged); per-controller 402 mapping; composer "Y" avatar and the "Public & anonymous" banner removed.

Tests: PHP 166/166, JS 145/145, phpcs + phpstan clean; bundle rebuilt and committed.

🤖 Generated with Claude Code

Robbertvermeulen and others added 7 commits September 19, 2026 11:50
… /topics proxy, chat flag

Adds the PHP-side plumbing for the v1 direct-submit flow (chat-free
question/bug/feature-request submissions moderated in the hub dashboard):

- Consumer::email(): ?string — feeds hub-side subject identity, never public.
- Config::$enableChat (default false) — the boot payload's chat-availability
  flag; when false, Roundtable::mount() no longer registers the chat/message
  REST routes (message, history, stream, session reset, case draft-from-chat).
  Cases/comments/votes/publish/topics routes always stay registered.
- HubClient::submitTopic() — POSTs /cases/direct with subject_id/type/title/
  body plus the Consumer's email/metadata/client_version/licence; hub's 402
  maps to the existing licence-invalid exception kind via the shared guard.
- TopicController — new POST <ns>/topics REST proxy mirroring
  PublishController's gating/error-mapping conventions.
- Admin\Page now localizes chatEnabled alongside the existing chatDisabled
  (hub-paused) flag for Task 4's UI to consume.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dialog

Chat panel, launch button, and backdrop now only render when
RoundtableConfig.chatEnabled is true (Task 3's boot flag); with it
false (the v1 default) the topics feed takes the full width and the
"+ New topic" button opens the existing PublishDialog in a new empty
"direct" variant (empty type selector, title, free-text body) instead
of the chat panel. Direct mode shows the moderation notice above the
submit button and POSTs to the new /topics proxy; on success it closes
the dialog, refreshes the lists, and switches to the Started tab,
matching the existing chat-drafted publish flow. Licence/quota errors
render inline via the same notice styling. Chat code stays in the
bundle — this is a render switch, not a deletion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… off

The empty-state copy in StartedList always said "Use <agent> on the
right to draft one", but with chatEnabled=false (the v1 default)
there's no chat panel on the right — and this tab is exactly where the
new direct-submit flow lands after a successful submission. Extract
the message into emptyStateMessage(chatOn, agentName) and switch copy
based on config.js's chatEnabled(): chat on keeps the existing
sentence, chat off points at the "+ New topic" button instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ed composer avatar

Two approved fixes from Robbert's live test round:

1. A pending (unapproved) case's votes and comments are refused
   hub-side (403), but the detail view and list rows still rendered
   VoteControl (fetches the vote tally on mount) and, on the detail
   view, fetched comments and rendered the comment composer — posting
   showed "Could not post. Try again." and the console filled with
   403s. VoteControl is now replaced by a new non-interactive
   StaticVote (no fetch) for any topic.isPending row/detail, shared by
   TopicRow (covers all list contexts: all/participating/roadmap/
   started) and TopicDetail. TopicDetail also skips the loadComments
   fetch entirely for a pending topic and shows one quiet line,
   "Comments open once this topic is approved.", in place of the
   comments list/composer. Draft rows are untouched (they never
   rendered VoteControl).

2. The comment composer rendered a hardcoded PersonAvatar
   initials="Y", while every other rendered identity in the app uses
   handle initials — read as two different people. Removed the
   composer's avatar (and its now-orphaned rt-addwrap/rt-you-ava CSS);
   the editor now spans the full width of the card like the rest of
   its layout.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, composer back

Robbert's browser review of the previous fix: a bare vote number next
to rows that still show the full arrow stack read as broken, and
hiding the comment composer entirely felt like more was missing than
intended. Rework per his direction:

- vote-control.jsx: StaticVote is now DisabledVote — same up-arrow /
  count / down-arrow markup as VoteControl, but the buttons carry
  disabled + aria-disabled and no click handlers, so pending rows and
  the pending detail header look structurally identical to a
  published one, just inert. Still no fetchVoteTally() call.
- topic-detail.jsx: the comment composer is back for a pending topic
  (textarea usable as normal) with only its submit button disabled;
  the "Comments open once this topic is approved." notice now sits
  directly above the composer instead of replacing the whole comments
  section. The comments list/header stay hidden (no fetchComments
  call — still no 403s) since there's nothing fetched to show.
- comment-editor.jsx: added a submitDisabled prop that forces the
  submit button off and blocks the Cmd/Ctrl+Enter submit shortcut,
  independent of the existing posting/text-empty checks.

Draft rows and the no-fetch behavior for pending votes/comments are
unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pre-release trust hardening (hub side, parallel task): comment create and
vote cast now enforce the licence gate like the chat message and direct-
submit paths, with 402 denial. Mirrors HubClient::submitTopic's approach:

- HubClient::createComment() / castVote() now send the licence payload
  (licenceField()) and the Consumer's email (emailField(), omit-when-null),
  reusing the same helpers submitTopic already uses. retractVote() and
  getVoteTally() are untouched (no body, no licence needed).
- CommentsController / VotesController map hub 402 to the existing
  licence-invalid exception kind at the same status/shape the topics proxy
  uses (added the missing LICENCE_INVALID => 402 arm to each statusFor()).

Scope note: the coordinator's brief also named "comment UPDATE" for licence
hardening. Verified against the codebase: no comment-update (or delete)
client method, REST route, or UI caller exists anywhere in this SDK today
(only create + list). The hub already has PATCH /cases/{id}/comments/{id}
(pre-existing, unrelated to this hardening task), but wiring it up would
mean adding a new Transport HTTP verb plus a new REST proxy route — new
feature surface, not "send an existing request one more field" — so it's
left out of this change pending explicit confirmation that's wanted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Approved pre-release cut: drop the banner entirely, no replacement
copy. .rt-banner CSS stays — still used by the pending-topic detail
banner (rt-banner-pending).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Robbertvermeulen
Robbertvermeulen merged commit cbe296d into main Sep 20, 2026
2 checks passed
@Robbertvermeulen
Robbertvermeulen deleted the feat/direct-submit-ui branch September 20, 2026 15:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant