feat(conversations): route in-app support submissions to conversations when flag on - #70726
Conversation
…s when flag on The product-support-side-panel flag only gated the side panel support tab, so the Max AI ticket/feedback prompts, early access feature-preview feedback, and the logged-out support modal kept creating Zendesk tickets even for users fully switched to conversations. Centralize the routing in supportLogic's submit listener: when the flag is on and the conversations widget is available, send the message as a new conversations ticket via posthog.conversations.sendMessage(); otherwise fall through to the unchanged Zendesk path. A null sendMessage response falls back to Zendesk (nothing was sent), while a thrown error does not, to avoid double-filing. Submits wait briefly for the lazily- loaded extension before falling back, and flag-on fallbacks are tagged so the volume is observable while Zendesk is retired. With the flag on, "contact support" CTAs open the side panel's own new-ticket composer, prefilled with any message (and exception context), and land the user in the created ticket's thread - the same experience as the sidebar. Other surfaces show a toast whose View button opens that thread. Conversations tickets carry just the message, so the form hides the Zendesk triage fields (message type, topic, severity), and the "engineer" wording is kept across the conversations surfaces. Generated-By: PostHog Code Task-Id: 3c8e8aac-4f86-4315-8b5c-2564f28720fb
🤖 CI report
|
| File | Size | Δ vs base |
|---|---|---|
toolbar/src/toolbar/debug/chunk-EventDebugMenu.js |
255.4 KiB | 🔺 +236.8 KiB (+1278.5%) |
render-query/src/render-query/render-query.js |
21.24 MiB | 🟢 -2.8 KiB (-0.0%) |
posthog-app/src/scenes/AuthenticatedShell.js |
222.4 KiB | 🔺 +1.6 KiB (+0.7%) |
Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report
✅ Eager graph — within budget
How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.
| Root | Eager (shipped) | Δ vs base | Budget |
|---|---|---|---|
entry (logged-out pages, app bootstrap)src/index.tsx |
1.21 MiB · 22 files | no change | ███░░░░░░░ 28.2% of 4.29 MiB |
authenticated shell (every logged-in page)src/scenes/AuthenticatedShell.tsx |
8.12 MiB · 2,975 files | 🔺 +3.8 KiB (+0.0%) | █████████░ 87.8% of 9.25 MiB |
🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
Largest files eagerly shipped from src/index.tsx
| Size | File |
|---|---|
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 24.6 KiB | ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js |
| 6.3 KiB | ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js |
| 4.5 KiB | ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js |
| 3.9 KiB | ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js |
| 1.4 KiB | ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js |
| 1.3 KiB | src/RootErrorBoundary.tsx |
| 912 B | ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js |
| 789 B | src/scenes/ChunkLoadErrorBoundary.tsx |
| 762 B | src/index.tsx |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
| Size | File |
|---|---|
| 278.6 KiB | ../node_modules/.pnpm/posthog-js@1.399.5/node_modules/posthog-js/dist/rrweb.js |
| 267.7 KiB | ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 234.9 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 222.1 KiB | ../node_modules/.pnpm/posthog-js@1.399.5/node_modules/posthog-js/dist/module.js |
| 164.0 KiB | src/queries/validators.js |
| 154.3 KiB | ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js |
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 105.9 KiB | src/lib/api.ts |
| 93.3 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
| 90.6 KiB | ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js |
Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479
✅ Dist folder size — 🟢 -1.09 MiB (-0.1%)
Total size of the built frontend/dist folder (all assets), compared against the base branch.
Total: 1284.86 MiB · 🟢 -1.09 MiB (-0.1%)
⚠️ Playwright — 1 failed
🎭 Playwright report · View test results →
❌ 1 failed test:
- Separates feature flag properties into their own tab (chromium)
These issues are not necessarily caused by your changes.
Annoyed by this section? Help fix flakies and failures and it will go green!
🦔 Hogbox preview · ✅ ready▶ Open the preview
commit |
|
Reviews (1): Last reviewed commit: "feat(conversations): route in-app suppor..." | Re-trigger Greptile |
…ails A failed feature-preview feedback submit showed an error toast but still cleared the textarea and collapsed the panel, losing the user's text. The submit path reports failure by not advancing lastSubmittedTicketId, so the feedback loader now keeps the panel open (and the draft) when the ticket wasn't created, and only clears it on success. Generated-By: PostHog Code Task-Id: 3c8e8aac-4f86-4315-8b5c-2564f28720fb
The PostHog AI ticket and feedback prompts only cleared their submitting state when lastSubmittedTicketId advanced (success). A failed submit left the spinner running / the modal hanging open with no way forward. Await the submit and, when no ticket was created, clear the pending state so the user can retry (the error toast already explains what happened). Generated-By: PostHog Code Task-Id: 3c8e8aac-4f86-4315-8b5c-2564f28720fb
…sations-support-routing
The new-ticket composer was keyed on a revision that also advanced on every editor change (onDraftChange routed typing back through the logic), so typing remounted the editor and dropped focus. Make the draft a one-shot seed for prefilled CTAs only: drop onDraftChange, bump the revision solely on injection, and clear the draft when leaving the composer so a later blank "New ticket" starts empty. Generated-By: PostHog Code Task-Id: 3c8e8aac-4f86-4315-8b5c-2564f28720fb
veryayskiy
left a comment
There was a problem hiding this comment.
Couple small fixes:
- supportLogic.ts (fallback comment body, ~L810): when the flag is on but the extension is unavailable / sendMessage returns null, the triage fields are hidden so kind/target_area are undefined, and the Zendesk ticket body renders literal Kind: undefined / Target area: undefined. Fall back to kind ?? 'support' / target_area ?? 'General' there like the subject line already does.
- sidepanelTicketsLogic.ts: on an enabled mount the re-consume runs twice — afterMount calls initTickets() (whose listener already re-consumes isEmailFormOpen/pendingViewTicket) and then afterMount re-consumes again itself. Harmless today because the guards are idempotent, but it's redundant; drop the re-consume from one of the two.
Stamped!
Problem
The
product-support-side-panelflag only gated the side panel support tab. Every other in-app route to support still created a Zendesk ticket even for users who are otherwise fully switched to conversations: the Max AI ticket and feedback prompts, early access feature-preview feedback, and the logged-out support modal on auth pages. So "flag on" didn't actually mean "support goes to conversations" everywhere.Changes
Centralizes the routing decision in
supportLogic's submit path. When the flag is on and the conversations widget is available, the message is sent as a new conversations ticket viaposthog.conversations.sendMessage(). Otherwise it falls through to the unchanged Zendesk path.nullsendMessage response falls back to Zendesk (nothing left the browser). A thrown error does not, to avoid double-filing a ticket that may already have reached the server.conversations_fallbackso the volume is observable while Zendesk is retired.Flag off is byte-identical to today. This is one of three PRs split out of a larger conversations/Zendesk-switchover branch and stands alone; the backend behavior for logged-out email replies lands in a sibling PR (anonymous widget email tickets).
Note
Logged-out users only get a coherent "reply by email" experience once the sibling anonymous-email-tickets PR is also live. The flag rarely matches logged-out users (org-targeted), and the toast copy is hedged, so the two can merge in either order before the flag is turned up for logged-out traffic.
How did you test this code?
Automated only (I, Claude, did not manually click through the UI):
supportLogictests: flag on routes to conversations (Zendesk transport not hit), flag off / extension unavailable / null response fall back to exactly one Zendesk ticket, a thrown sendMessage does not fall back, the submit waits for the lazily-loaded extension before falling back, and exception context is preserved on the conversations message. Suite passes.sidepanelTicketsLogictests: a support CTA opens the composer prefilled (both when the panel is already mounted and when it mounts fresh), and a submission toast's View opens the specific ticket thread. Suite passes.Automatic notifications
🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Abigail directed this; I (Claude, via PostHog Code) wrote it. Carved out of a larger switchover branch. Skills invoked:
/writing-tests(gating the logic tests) and/adopting-generated-api-typescontext for the frontend touchpoints.Key decisions, several from a multi-agent review pass on the original branch: the null-vs-throw distinction on
sendMessageis deliberate (null means nothing was sent, so Zendesk fallback is safe; a throw might mean the server already got it, so we don't fall back); support CTAs drive the sidebar's ownNewTicketcomposer via a consumed intent rather than a second composer implementation, so there's one composer and one post-submit destination; and the submit waits for the lazily-loaded extension (with a tagged fallback) rather than racing it to Zendesk. Conversations tickets stay message-only because the agent ticket view already surfaces session replay and session-scoped exceptions for widget-channel tickets — the appended exception line is a belt-and-suspenders for email-channel tickets and cases where the session can't resolve it.Created with PostHog Code