feat(ui): move channel config into dialogs + VoIP surface (#19)#1348
Conversation
Part of the Access/Sharing redesign (Epic trinity-enterprise#16). The Access tab (#17) already owns Trinity operators; this scopes the Sharing tab to the operator → external-client surface. - Google-Docs-style "Share this agent" framing; operator language removed (operators live on the Access tab). - External access policy collapsed into one **Restricted ↔ Open** segmented control over require_email/open_access (Restricted = approval-gated, Open = anyone verified; identity proof always on for external sharing). - Pending requests kept, reframed as external clients awaiting approval. - Channels rendered as compact collapsible summary rows (new ChannelDisclosure.vue). Detailed config stays reachable inside the expanded row as a non-regressing interim seam — #19 replaces the body with a modal dialog. No channel functionality removed. - Outbound file sharing + public links nudged into a separate "Distribution" section (distribution, not client access). Frontend-only; no API changes. SharingPanel prop/emit contract unchanged. Verified: both SFCs compile (vue/compiler-sfc), design-token check passes. Related to Abilityai/trinity-enterprise#18 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builds on the Sharing-tab reframe (#18). Replaces the inline collapsible channel panels with compact summary rows that open the existing config panels in a modal — the tab stays a clean summary instead of a wall of forms. - New ChannelConfigDialog.vue — Teleport modal (backdrop + Esc close, scrollable body) matching the ConfirmDialog idiom. - New ChannelConfigRow.vue — summary row showing connected/off + a brief status (channel/handle/number), a Configure/Manage button that opens the dialog, and a lightweight status fetch (shared `api` client) that refetches on dialog close so the row reflects edits. The 4 channel panels render untouched in the dialog slot — no loss of functionality. - SharingPanel wires Slack/Telegram/WhatsApp/VoIP through ChannelConfigRow with per-channel status derivations. VoIP (previously backend-only) now has its Sharing UI surface, gated on `voip_available`. - Distribution (public links, file sharing) keeps the collapsible disclosure — out of scope for the channel-dialog change. Frontend-only; no API changes. Verified: 3 SFCs compile (vue/compiler-sfc), design-token check passes, and Vite transforms all modules on the live dev server (HTTP 200). Related to Abilityai/trinity-enterprise#19 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…llapse) A closed ChannelConfigDialog should collapse to Teleport comment nodes, but under some dev-server HMR states its slotted panel rendered inline as a flex child of the channel row, squeezing the label (esp. WhatsApp, whose panel is the widest) to zero width. Gate the dialog with v-if="dialogOpen" in ChannelConfigRow so the closed dialog — and its slotted panel — never participates in the row's flex layout, and mount the panel on demand. Make the dialog's Escape handler bind immediately so it still works when mounted already-open. Related to Abilityai/trinity-enterprise#19 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| class="inline-block w-2 h-2 rounded-full" | ||
| :class="status.warn ? 'bg-status-warning-500' : 'bg-status-success-500'" | ||
| ></span> | ||
| <span class="text-gray-600 dark:text-gray-300 truncate">{{ status.label || 'Connected' }}</span> |
There was a problem hiding this comment.
[Medium] Status label won’t truncate — long labels overflow the row
This truncate span is a flex child without min-w-0, so it can’t shrink below its content width and a long status label (e.g. WhatsApp’s whatsapp:+14155238886) overflows the row instead of clipping. Same flexbox class of bug the dialog v-if fix addressed, but on the inner status line — latent until a channel actually connects.
| <span class="text-gray-600 dark:text-gray-300 truncate">{{ status.label || 'Connected' }}</span> | |
| <span class="text-gray-600 dark:text-gray-300 truncate min-w-0">{{ status.label || 'Connected' }}</span> |
Optional polish: add shrink-0 to the status dots (lines 11/16/23) and the “· setup needed” span (line 20) so they don’t squish when the label is long.
| <!-- Config dialog: mounted only while open so the closed dialog (and its | ||
| slotted panel) never participates in this row's flex layout. The | ||
| channel panel renders inside the modal, untouched. --> | ||
| <ChannelConfigDialog v-if="dialogOpen" :open="dialogOpen" :title="title" :icon="icon" @close="onDialogClose"> |
There was a problem hiding this comment.
[Low] Redundant triple-gating of the dialog
Now that the dialog is mounted only via v-if="dialogOpen", both :open="dialogOpen" here and ChannelConfigDialog’s internal v-if="open" are always true while mounted — dead weight. Consider either dropping this parent v-if (rely on the internal Teleport + v-if, the original approach) or dropping the open prop + internal v-if (rely purely on mount/unmount). All three together works but is confusing. Not a bug.
| @@ -0,0 +1,67 @@ | |||
| <template> | |||
| <Teleport to="body"> | |||
| <div v-if="open" class="fixed z-50 inset-0 overflow-y-auto" role="dialog" aria-modal="true" :aria-label="title"> | |||
There was a problem hiding this comment.
[Low] Modal a11y: no focus management or scroll lock
role="dialog" aria-modal="true" is set, but focus isn’t moved into the dialog on open, there’s no focus trap, and background scroll isn’t locked. This matches the existing ConfirmDialog.vue, so it’s not a regression — flagging for parity if a shared modal primitive is extracted later.
…log gating Addresses code-review findings on PR #1348: - [Medium] The connected-status label was a flex child without `min-w-0`, so a long label (e.g. WhatsApp `whatsapp:+14155238886`) overflowed the row instead of clipping. Add `min-w-0` to the label and `shrink-0` to the status dots and the "· setup needed" span. - [Low] Collapse the redundant triple-gating to a single mechanism: the dialog is mounted only via the parent `v-if="dialogOpen"`, so drop the now always-true `:open` prop and ChannelConfigDialog's internal `v-if="open"` (Escape binds for the component's lifetime via onMounted/onUnmounted). (Skipped the a11y note — focus-trap/scroll-lock would be new behavior beyond the existing ConfirmDialog parity; not a regression.) Related to Abilityai/trinity-enterprise#19 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Applied the review fixes in 304c1a1:
Verified: both SFCs compile ( |
…l lock (#19 review) Addresses the open review finding: the dialog had Esc-to-close but no focus management. Now on open it moves focus into the dialog (first focusable, else the panel), traps Tab within it (wrap-around), locks background scroll, and restores focus to the opener (Configure/Manage button) + the prior body overflow on close. Findings #1 (status-label truncation) and #2 (redundant dialog gating) were already addressed in earlier commits. ConfirmDialog.vue shares the same gap; a shared modal primitive applying this to both is the cleaner longer-term fix. Verified: 3 SFCs compile, check:tokens passes, clean npm ci + vite build green.
Review + fixes appliedRe-reviewed the diff and applied the outstanding proposed fix. Status of the three earlier findings:
Fresh-pass notes (no blockers)
Note
Verified: 3 SFCs compile ( |
Resolves SharingPanel.vue against dev drift since feature/18 merged: - keep dev's Public chat model (#894), Additional instructions (#1205), MCP connector row (ent#46), and Client roster (#20) sections - re-apply the #19 delta: ChannelDisclosure -> ChannelConfigRow for Slack/Telegram/WhatsApp/VoIP with status derivations - MCP connector row stays a ChannelDisclosure (predates #19's row pattern)
vybe
left a comment
There was a problem hiding this comment.
Validated via /validate-pr: effective diff vs dev is exactly the 3 intended files (2 new SFCs + SharingPanel channels-section swap); all 3 SFCs compile; security greps clean; no API/schema/config surface. Resolved the dev-drift merge conflict (kept #894/#1205/ent#46/#20 sections, re-applied the #19 ChannelConfigRow delta; MCP-connector row stays a ChannelDisclosure as it predates the row pattern). Cross-tracker ent#19 needs a manual status-in-dev bump after merge.
Implements trinity-enterprise#19 (Access/Sharing redesign epic, trinity-enterprise#16). Frontend-only; no API changes.
Replaces #18's interim collapsible channel rows with the dialog pattern the issue calls for: the tab stays a clean summary; full config opens in a modal.
Changes
ChannelConfigDialog.vue— Teleport modal (backdrop + Esc close, scrollable body) matching theConfirmDialogidiom.ChannelConfigRow.vue— compact summary row: channel glyph, connected/off + brief status (Slack#channel, Telegram@bot, WhatsApp/VoIP number, VoIP "(disabled)"/setup-needed warnings), and a Configure/Manage button that opens the dialog. A lightweight status GET (sharedapiclient) refetches on dialog close so the row reflects edits.SharingPanel.vue— wires Slack / Telegram / WhatsApp / VoIP throughChannelConfigRowwith per-channel status derivations. The 4 existing panels render untouched inside the dialog slot (no loss of functionality). VoIP — previously backend-only — now has its Sharing UI surface (gated onvoip_available).Acceptance criteria
voip_available)Verification
vue/compiler-sfc;npm run check:tokenspasses.🤖 Generated with Claude Code