-
Notifications
You must be signed in to change notification settings - Fork 66
feat: add prebuilt templates to support form #316
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
beran-t
wants to merge
1
commit into
main
Choose a base branch
from
support-templates
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,146 @@ | ||
| export const SUPPORT_TEMPLATES = [ | ||
| { | ||
| id: 'sandbox_issue', | ||
| label: 'Issue with a sandbox', | ||
| title: 'Sandbox issue', | ||
| prefill: `Sandbox ID: | ||
| What you were trying to do: | ||
| What went wrong (error message, logs, or unexpected behavior): | ||
| Approximate time it happened (UTC if possible): | ||
| Steps to reproduce, if known: `, | ||
| }, | ||
| { | ||
| id: 'snapshot_pause_resume', | ||
| label: 'Snapshot, pause, or resume failed', | ||
| title: 'Snapshot / pause / resume issue', | ||
| prefill: `Sandbox ID: | ||
| Snapshot or template ID (if applicable): | ||
| Which action failed (snapshot creation, pause, resume): | ||
| Error message returned: | ||
| Approximate time it happened (UTC if possible): `, | ||
| }, | ||
| { | ||
| id: 'slow_performance', | ||
| label: 'Performance is slow', | ||
| title: 'Performance issue', | ||
| prefill: `Sandbox ID(s): | ||
| What is slow (sandbox start, command execution, network, file system, etc.): | ||
| How long it currently takes: | ||
| How long you expect it to take: | ||
| Approximate time of a recent slow run (UTC if possible): `, | ||
| }, | ||
| { | ||
| id: 'cancel_subscription', | ||
| label: 'Cancel my subscription', | ||
| title: 'Subscription cancellation', | ||
| prefill: `Team ID: | ||
| Email on the payment method: | ||
| Reason for canceling (optional, helps us improve): `, | ||
| }, | ||
| { | ||
| id: 'refund_or_credit', | ||
| label: 'Refund or credit request', | ||
| title: 'Refund / credit request', | ||
| prefill: `Team ID: | ||
| Invoice number or charge date: | ||
| Amount: | ||
| Reason for the request: `, | ||
| }, | ||
| { | ||
| id: 'delete_account', | ||
| label: 'Delete my account', | ||
| title: 'Account deletion request', | ||
| prefill: `Team ID: | ||
| Confirm you want all account data permanently deleted (yes / no): | ||
| Anything else we should know: `, | ||
| }, | ||
| { | ||
| id: 'change_account_owner', | ||
| label: 'Change account owner or team email', | ||
| title: 'Account owner change', | ||
| prefill: `Team ID: | ||
| Current owner email: | ||
| New owner email: | ||
| Confirm the new owner has already created an E2B account with the new email (yes / no): `, | ||
| }, | ||
| { | ||
| id: 'increase_limit', | ||
| label: 'Increase a limit (concurrency, RAM, disk, build)', | ||
| title: 'Limit increase request', | ||
| prefill: `Team ID: | ||
| Which limit you want raised (concurrent sandboxes, RAM per sandbox, disk per sandbox, concurrent builds): | ||
| Current value: | ||
| Target value: | ||
| Brief use case: `, | ||
| }, | ||
| { | ||
| id: 'volumes_access', | ||
| label: 'Volumes access', | ||
| title: 'Volumes access request', | ||
| prefill: `Team ID: | ||
| What you plan to use volumes for: | ||
| Approximate volume size and access pattern (e.g. 50 GB read-mostly, shared across sandboxes): | ||
| Region preference (currently us-west1 only): `, | ||
| }, | ||
| { | ||
| id: 'eu_cluster', | ||
| label: 'EU cluster or data residency', | ||
| title: 'EU cluster / data residency request', | ||
| prefill: `Team ID: | ||
| Specific data residency requirement (region, regulation, customer ask): | ||
| Expected sandbox volume per month: | ||
| Timeline: `, | ||
| }, | ||
| { | ||
| id: 'enterprise_inquiry', | ||
| label: 'Enterprise inquiry', | ||
| title: 'Enterprise inquiry', | ||
| prefill: `Company: | ||
| Use case: | ||
| Current scale (sandboxes per day or month): | ||
| Expected scale at 6 months: | ||
| Timeline: `, | ||
| }, | ||
| { | ||
| id: 'self_hosting', | ||
| label: 'Self-hosting / BYOC', | ||
| title: 'Self-hosting / BYOC inquiry', | ||
| prefill: `Company: | ||
| Cloud provider you want to deploy on: | ||
| Why self-hosted is required (compliance, data residency, latency, etc.): | ||
| Expected scale: `, | ||
| }, | ||
| { | ||
| id: 'startup_program', | ||
| label: 'Startup program', | ||
| title: 'Startup program inquiry', | ||
| prefill: `Company: | ||
| Stage (pre-seed, seed, Series A, etc.): | ||
| Current E2B usage (if any): | ||
| What you are building: `, | ||
| }, | ||
| { | ||
| id: 'something_else', | ||
| label: 'Something else', | ||
| title: 'Support Request', | ||
| prefill: '', | ||
| }, | ||
| ] as const | ||
|
|
||
| export type SupportTemplate = (typeof SUPPORT_TEMPLATES)[number] | ||
| export type SupportTemplateId = SupportTemplate['id'] | ||
|
|
||
| export const SUPPORT_TEMPLATE_IDS = SUPPORT_TEMPLATES.map((t) => t.id) as [ | ||
| SupportTemplateId, | ||
| ...SupportTemplateId[], | ||
| ] | ||
|
|
||
| export const DEFAULT_SUPPORT_TEMPLATE_ID: SupportTemplateId = 'something_else' | ||
|
|
||
| export function getSupportTemplate(id: SupportTemplateId): SupportTemplate { | ||
| const template = SUPPORT_TEMPLATES.find((t) => t.id === id) | ||
| if (!template) { | ||
| throw new Error(`Unknown support template id: ${id}`) | ||
| } | ||
| return template | ||
| } |
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
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
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 When a user picks a non-default template (so the prefill is applied) and then switches back to Something else,
handleTemplateChangerunsform.setValue('description', '', { shouldValidate: true }). The empty string immediately fails thedescription.min(1)rule andFormMessagerenders Please describe how we can help under the textarea before the user has typed anything. Consider passingshouldValidate: false(or skipping thesetValuecall) when transitioning to the empty default — the field will still be invalid, so the submit button stays disabled, but the error message won't appear until the user has actually had a chance to type.Extended reasoning...
What happens
In
report-issue-dialog.tsx:149-163,handleTemplateChangeruns:When
nextPrefillis the empty string (onlysomething_elsehasprefill: '') and the second branch (current === lastPrefillRef.current) is hit, the description gets cleared withshouldValidate: true. RHF runs the resolver synchronously, the zoddescription.min(1, 'Please describe how we can help')rule fails, andformState.errors.descriptionis populated.Why the message renders
The shared
FormMessage(src/ui/primitives/form.tsx) renders any error infieldStateunconditionally — it does not gate ontouchedordirty. So the momentshouldValidate: trueproduces an error, the textarea grows a Please describe how we can help line under it, even though the user has not typed anything in this category and just used the picker.Step-by-step proof
description = '',templateId = 'something_else',lastPrefillRef = ''. No error (RHF default mode isonSubmit, nothing has triggered validation).current === ''→ setValue with the sandbox prefill.shouldValidate: truevalidates, but the prefill is non-empty somin(1)passes.lastPrefillRef.currentbecomes the sandbox prefill.nextPrefill = '',current === lastPrefillRef.currentis true, soform.setValue('description', '', { shouldValidate: true })runs.min(1)fails →formState.errors.descriptionis set →FormMessagerenders the error under the still-empty textarea.Addressing the counter-argument
It is true that (a) submission relies on
form.formState.isValid, so the field must validate, and (b) eager inline validation is not by itself an anti-pattern. The issue is specifically the timing: the error surfaces in response to a category change, not in response to user input on that field. The user has not had any opportunity to enter content for the new category before being told it is missing. That distinguishes it from the typical eager-validation cases (validate on blur, on submit, or after first keystroke).Fix
Either pass
shouldValidate: falsewhen applying an empty prefill, or skip thesetValueentirely whennextPrefill === ''(and clear by other means if needed). The form will remain invalid (the existing!form.formState.isValidguard already disables the Send button regardless of whether the resolver has run), but the visible error won't appear until the user actually engages with the textarea. Marking as nit — minor visible UX papercut, no functional/correctness impact.