feat(conversations): make widget ticket recovery text customizable - #90802
feat(conversations): make widget ticket recovery text customizable#90802christiaan-ph wants to merge 5 commits into
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
🤖 CI report
|
| File | Size | Δ vs base |
|---|---|---|
posthog-app/_parent/products/engineering_analytics/frontend/scenes/EngineeringAnalyticsScene.js |
71.7 KiB | 🔺 +9.9 KiB (+16.1%) |
render-query/src/render-query/render-query.js |
21.76 MiB | 🔺 +1.6 KiB (+0.0%) |
posthog-app/src/scenes/models/NodeDetailScene.js |
15.6 KiB | 🟢 -1.5 KiB (-8.5%) |
exporter/src/exporter/scenes/ExporterNotebookScene.js |
3.40 MiB | 🟢 -1.3 KiB (-0.0%) |
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.37 MiB · 22 files | no change | ███░░░░░░░ 30.3% of 4.51 MiB |
authenticated shell (every logged-in page)src/scenes/AuthenticatedShell.tsx |
8.82 MiB · 3,247 files | 🔺 +683 B (+0.0%) | █████████░ 90.8% of 9.71 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 |
|---|---|
| 307.0 KiB | ../node_modules/.pnpm/posthog-js@1.422.0_@types+react@18.3.27_react@18.3.1/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 |
| 263.4 KiB | ../node_modules/.pnpm/posthog-js@1.422.0_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.js |
| 252.5 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 154.2 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 |
| 104.7 KiB | src/lib/api.ts |
| 95.2 KiB | ../packages/quill/packages/quill/dist/index.js |
| 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
✅ Toolbar bundle — eager 2.26 MiB within budget
What the toolbar ships to customer pages, measured from the esbuild output (minified, post-tree-shake). The eager set is the entry plus everything statically imported from it — fetched before any feature runs; deferred chunks load lazily. The eager guardrail is 5.72 MiB. Each output file must also stay below 10 MB, where CloudFront stops compressing it. The module boundary is enforced separately by check-toolbar-graph.
| Metric | Size | Δ vs base | Budget |
|---|---|---|---|
| Eager (shipped) entry + static imports |
2.26 MiB · 18 files | 🔺 +52 B (+0.0%) | ████░░░░░░ 39.5% of 5.72 MiB |
| Deferred (lazy) | 2.11 MiB · 45 files | no change | n/a — loads on demand |
Loader dist/toolbar.js |
1.1 KiB | no change | █░░░░░░░░░ 5.8% of 19.5 KiB |
Largest eagerly-shipped chunks
| Size | File |
|---|---|
| 749.3 KiB | dist/toolbar/toolbar-app-J2MBJBF4.css |
| 588.4 KiB | dist/toolbar/chunk-chunk-SFURV3BL.js |
| 484.7 KiB | dist/toolbar/chunk-chunk-Y2PMGK4R.js |
| 133.8 KiB | dist/toolbar/chunk-chunk-6NWA4NUR.js |
| 131.8 KiB | dist/toolbar/chunk-chunk-FDH2IBXT.js |
| 71.3 KiB | dist/toolbar/toolbar-app-BXSMFDC7.js |
| 69.0 KiB | dist/toolbar/chunk-chunk-TSAL54PB.js |
| 35.6 KiB | dist/toolbar/chunk-chunk-2SIN7665.js |
| 20.9 KiB | dist/toolbar/chunk-chunk-UT2FXBCX.js |
| 6.8 KiB | dist/toolbar/chunk-chunk-DV7IWQNF.js |
Posted automatically by check-toolbar-size · sizes are toolbar output bytes (shipped, post-tree-shake) from the esbuild metafile
✅ Dist folder size — 🔺 +84.7 KiB (+0.0%)
Total size of the built frontend/dist folder (all assets), compared against the base branch.
Total: 1435.19 MiB · 🔺 +84.7 KiB (+0.0%)
⚠️ Playwright — 1 flaky
🎭 Playwright report · View test results →
- Hover chart to see tooltip with data point values (chromium)
These issues are not necessarily caused by your changes.
Annoyed by this section? Help fix flakies and failures and it will go green!
Rename the "Ticket recovery link" label to "Ticket recovery link text" so it reads as the link's wording rather than a URL, and explain under both ticket recovery fields when the footer is shown. Associate every widget settings label with its input via htmlFor/id and give each Save button a distinct accessible name. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…copy Remove the htmlFor/id label association and Save button aria-labels so the form keeps one labeling pattern throughout. Widget form accessibility is tracked as team follow-up work. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
🦔 Hogbox preview · ✅ ready▶ Open the preview
commit |
Prompt To Fix All With AI### Issue 1
products/conversations/frontend/scenes/settings/supportSettingsLogic.ts:1701-1702
**Unrelated updates erase recovery drafts**
When a user enters either recovery-text draft and then changes another widget setting such as color, position, enabled state, or required email, that setting's `updateCurrentTeamSuccess` resets both draft values to `null`, causing the inputs to revert to their persisted text and silently discard the user's edits.
### Issue 2
products/conversations/frontend/scenes/settings/WidgetSection.tsx:199-207
**Recovery settings remain undocumented**
This adds two user-facing settings and corresponding remote-config properties without updating the matching product documentation, leaving users and integrators unable to discover the newly supported configuration there.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "fix(conversations): scope widget setting..." | Re-trigger Greptile |
| actions.setIdentificationFormTitleValue(null) | ||
| actions.setIdentificationFormDescriptionValue(null) |
There was a problem hiding this comment.
Unrelated updates erase recovery drafts
When a user enters either recovery-text draft and then changes another widget setting such as color, position, enabled state, or required email, that setting's updateCurrentTeamSuccess resets both draft values to null, causing the inputs to revert to their persisted text and silently discard the user's edits.
Prompt To Fix With AI
This is a comment left during a code review.
Path: products/conversations/frontend/scenes/settings/supportSettingsLogic.ts
Line: 1701-1702
Comment:
**Unrelated updates erase recovery drafts**
When a user enters either recovery-text draft and then changes another widget setting such as color, position, enabled state, or required email, that setting's `updateCurrentTeamSuccess` resets both draft values to `null`, causing the inputs to revert to their persisted text and silently discard the user's edits.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
Confirmed this is a real issue, and it predates this PR. The updateCurrentTeamSuccess handler clears every draft input (greeting, placeholder, both identification form fields, both recovery fields, and the Slack copy fields) to null after any successful team update. Because each input falls back to its saved value when the draft is null, an unsaved draft is silently dropped the moment another setting saves, including the color/position/enabled/require-email controls that save immediately when toggled. This PR only added the two recovery fields to that existing reset list, following the same save-per-field pattern as greeting and placeholder. A proper fix resets only the field that was actually saved, but the current save flow sends the whole settings object every time, so the success handler can't tell which field changed without restructuring how saves work — a change that should apply consistently to all the copy fields, not just the two recovery ones. That's a design decision beyond this PR's scope, so I'm escalating rather than making a recovery-only patch that would behave differently from its sibling fields. What a maintainer needs to decide: whether to redesign the draft-reset so unrelated saves keep in-progress drafts (e.g. each save clears only its own draft), applied across all widget copy fields, ideally alongside the concurrent-save handling from the related thread. Note the loading-guard fix on the recovery Save buttons does not address this, since a toggle elsewhere still triggers the reset.
There was a problem hiding this comment.
Agreed. This is a real, pre-existing issue in the shared copy-field save flow, and fixing only the recovery fields would create inconsistent behavior with greeting, placeholder, identification, and Slack copy. Keeping it as a follow-up for a coordinated draft-reset redesign is the right scope decision; the recovery Save-button loading guard does not address resets triggered by unrelated immediate-save controls.
🦔 PostHog Review reviewed this pull requestFound 0 must fix, 2 should fix, 0 consider. Published 2 findings (view the review). Resolved comments: 2 fixed, 1 declined, 1 left for you |
|
PostHog Review alpha 🦔 If you find any issues helpful - please reply "valid", "invalid", etc., for evaluation purposes 🙏 |
…erwrite
The two recovery Save buttons stayed clickable during an in-flight team
update. Because each save spreads the same conversations_settings snapshot
and the server merge is client-wins, a second concurrent save reverted the
first, losing the value silently.
Set loading={currentTeamLoading} on both recovery Save buttons so an
in-flight save disables them, matching the double-submission guard in
SlackSection and the repo convention.
Generated-By: PostHog Desktop
Task-Id: 0a532af1-05d4-4511-9f72-2b09847186d8
The description said the recovery footer is hidden from "identified" visitors. In PostHog "identified" means posthog.identify(), which does not hide the footer. The footer is hidden only for visitors using identity verification (a signed identity_distinct_id + identity_hash), matching the sibling in-app footer gate and the wording in the Identity verification settings section. Reword to "Only shown to visitors who aren't using identity verification" so a team running a logged-in product does not wrongly skip translating it. Generated-By: PostHog Desktop Task-Id: 0a532af1-05d4-4511-9f72-2b09847186d8
Problem
Teams that run the support widget in another language can customize the greeting and the input placeholder, but the ticket recovery footer ("Don't see your previous tickets? Recover them here") stays hardcoded in English. A customer running the widget in a non-English product asked for a way to translate it.
Changes
ticketRecoveryTextandticketRecoveryLinkTextin theconversationsblock, defaulting to the current strings.How did you test this code?
test_conversations_enabled_with_defaultsandtest_conversations_enabled_with_custom_configintest_remote_config.py. They catch remote config dropping the default strings or ignoring the stored settings. Both pass locally.conversations_settings, and confirmed the widget (on the companion branch) renders both custom recovery strings in the footer. The widget serves a cached config, so a hard refresh is needed to see fresh saves locally.Automatic notifications
Docs update
None.
🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Authored with Claude Code from a support request. Skills invoked: /writing-tests, /writing-user-facing-copy, /writing-pr-descriptions, /review-code. The session verified the change end to end on a local stack (settings UI save flow, Postgres persistence, remote config output). The follow-up commits apply copy findings from a multi-agent review: label wording and field descriptions. An accessibility pass over the widget form (label association, distinct Save button names) was drafted and then pulled back out; it is tracked as team follow-up work instead. No customer material appears in the diff; the test strings are invented.