feat(studio): clean up support form message (#42174)

* feat(studio): clean up support form message

Support form message currently has the studio version and dashboard logs
link appended to the end. On request from the Support team, we're moving
this to metadata fields so it won't be as distracting.

* Hide IncidentAdmonition if event is maintenance

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
CharisandJoshen Lim authored and GitHub committed 2026-01-27 08:50:52 -05:00
1 parent eb85515acb
commit 9caa0d548a
5 files changed
+36 -34

No files matched your search

@@ -54,7 +54,8 @@ const getStatusDescription = (
}
export function IncidentAdmonition({ isActive }: IncidentAdmonitionProps) {
const { data: incidents, isLoading, isError } = useIncidentStatusQuery()
const { data: allStatusPageEvents = [], isLoading, isError } = useIncidentStatusQuery()
const incidents = allStatusPageEvents.filter((x) => x.impact !== 'maintenance')
// Don't render anything while loading, on error, or if no incidents
if (isLoading || isError || !incidents || incidents.length === 0) {
@@ -27,24 +27,23 @@ export const formatMessage = ({
message,
attachments = [],
error,
commit,
dashboardLogUrl,
}: {
message: string
attachments?: string[]
attachments?: Array<string>
error: string | null | undefined
commit: { commitSha: string; commitTime: string } | undefined
dashboardLogUrl?: string
}) => {
const errorString = error != null ? `\n\nError: ${error}` : ''
const attachmentsString =
attachments.length > 0 ? `\n\nAttachments:\n${attachments.join('\n')}` : ''
const commitString =
commit != undefined
? `\n\n---\nSupabase Studio version: SHA ${commit.commitSha} deployed at ${commit.commitTime === 'unknown' ? 'unknown time' : dayjs(commit.commitTime).format('YYYY-MM-DD HH:mm:ss Z')}`
: ''
const logString = dashboardLogUrl ? `\nDashboard logs: ${dashboardLogUrl}` : ''
return `${message}${errorString}${attachmentsString}${commitString}${logString}`
return `${message}${errorString}${attachmentsString}`
}
export const formatStudioVersion = (commit: { commitSha: string; commitTime: string }): string => {
const formattedTime =
commit.commitTime === 'unknown'
? 'unknown time'
: dayjs(commit.commitTime).format('YYYY-MM-DD HH:mm:ss Z')
return `SHA ${commit.commitSha} deployed at ${formattedTime}`
}
export function getPageIcon(page: Page) {
@@ -32,6 +32,7 @@ import type { SupportFormValues } from './SupportForm.schema'
import type { SupportFormActions, SupportFormState } from './SupportForm.state'
import {
formatMessage,
formatStudioVersion,
getOrgSubscriptionPlan,
NO_ORG_MARKER,
NO_PROJECT_MARKER,
@@ -141,8 +142,6 @@ export const SupportFormV2 = ({ form, initialError, state, dispatch }: SupportFo
message: values.message,
attachments,
error: initialError,
commit,
dashboardLogUrl: dashboardLogUrl?.[0],
}),
verified: true,
tags: ['dashboard-support-form'],
@@ -155,6 +154,8 @@ export const SupportFormV2 = ({ form, initialError, state, dispatch }: SupportFo
.map((x) => x.trim().replace(/ /g, '_').toLowerCase())
.join(';'),
browserInformation: detectBrowser(),
dashboardLogs: dashboardLogUrl?.[0],
dashboardStudioVersion: commit ? formatStudioVersion(commit) : undefined,
}
if (values.projectRef !== NO_PROJECT_MARKER) {
@@ -86,9 +86,7 @@ const { mockCommitSha, mockCommitTime, mockUseDeploymentCommitQuery } = vi.hoist
}
})
const supportVersionInfo = `\n\n---\nSupabase Studio version: SHA ${mockCommitSha} deployed at ${dayjs(
mockCommitTime
).format('YYYY-MM-DD HH:mm:ss Z')}`
const mockStudioVersion = `SHA ${mockCommitSha} deployed at ${dayjs(mockCommitTime).format('YYYY-MM-DD HH:mm:ss Z')}`
vi.mock('react-inlinesvg', () => ({
__esModule: true,
@@ -810,10 +808,9 @@ describe('SupportFormPage', () => {
siteUrl: 'https://project-1.example.com',
additionalRedirectUrls: 'https://project-1.example.com/callbacks',
browserInformation: 'Chrome',
dashboardStudioVersion: mockStudioVersion,
})
const expectedMessage =
'Requests return status 500 when calling the RPC endpoint' + supportVersionInfo
expect(payload.message).toBe(expectedMessage)
expect(payload.message).toBe('Requests return status 500 when calling the RPC endpoint')
await waitFor(() => {
expect(screen.getByRole('heading', { name: /support request sent/i })).toBeInTheDocument()
@@ -901,9 +898,9 @@ describe('SupportFormPage', () => {
siteUrl: 'https://project-2.supabase.dev',
additionalRedirectUrls: 'https://project-2.supabase.dev/redirect',
browserInformation: 'Chrome',
dashboardStudioVersion: mockStudioVersion,
})
const expectedMessage = 'MFA challenge fails with an unknown error code' + supportVersionInfo
expect(payload.message).toBe(expectedMessage)
expect(payload.message).toBe('MFA challenge fails with an unknown error code')
await waitFor(() => {
expect(screen.getByRole('heading', { name: /support request sent/i })).toBeInTheDocument()
@@ -1002,10 +999,10 @@ describe('SupportFormPage', () => {
siteUrl: 'https://project-3.apps.supabase.co',
additionalRedirectUrls: 'https://project-3.apps.supabase.co/auth',
browserInformation: 'Chrome',
dashboardStudioVersion: mockStudioVersion,
})
expect(payload.message).toBe(
'Connections time out after 30 seconds\n\nError: Connection timeout detected' +
supportVersionInfo
'Connections time out after 30 seconds\n\nError: Connection timeout detected'
)
await waitFor(() => {
@@ -1381,10 +1378,9 @@ describe('SupportFormPage', () => {
})
const payload = submitSpy.mock.calls[0]?.[0]
expect(payload.message).toContain('Navigation menu does not respond after latest deploy')
expect(payload.message).toMatch(
/Dashboard logs: https:\/\/storage\.example\.com\/signed\/.+\.json/
)
expect(payload.message).toBe('Navigation menu does not respond after latest deploy')
expect(payload.dashboardLogs).toMatch(/^https:\/\/storage\.example\.com\/signed\/.+\.json$/)
expect(payload.dashboardStudioVersion).toBe(mockStudioVersion)
})
test('shows toast on submission error and allows form re-editing and resubmission', async () => {
@@ -1455,10 +1451,9 @@ describe('SupportFormPage', () => {
const payload = submitSpy.mock.calls[0]?.[0]
expect(payload.subject).toBe('Cannot access settings')
expect(payload.message).toMatch(
'Settings page shows 500 error - updated description' + supportVersionInfo
)
expect(payload.message).toMatch(/Dashboard logs: https:\/\/storage\.example\.com\/.+\.json/)
expect(payload.message).toBe('Settings page shows 500 error - updated description')
expect(payload.dashboardLogs).toMatch(/^https:\/\/storage\.example\.com\/signed\/.+\.json$/)
expect(payload.dashboardStudioVersion).toBe(mockStudioVersion)
await waitFor(() => {
expect(toastSuccessSpy).toHaveBeenCalledWith('Support request sent. Thank you!')
@@ -1684,9 +1679,9 @@ describe('SupportFormPage', () => {
verified: true,
tags: ['dashboard-support-form'],
browserInformation: 'Chrome',
dashboardStudioVersion: mockStudioVersion,
})
const expectedMessage = 'I need help accessing my Supabase account' + supportVersionInfo
expect(payload.message).toBe(expectedMessage)
expect(payload.message).toBe('I need help accessing my Supabase account')
await waitFor(() => {
expect(screen.getByRole('heading', { name: /support request sent/i })).toBeInTheDocument()
@@ -20,6 +20,8 @@ export type sendSupportTicketVariables = {
siteUrl?: string
additionalRedirectUrls?: string
dashboardSentryIssueId?: string
dashboardLogs?: string
dashboardStudioVersion?: string
}
export async function sendSupportTicket({
@@ -36,6 +38,8 @@ export async function sendSupportTicket({
siteUrl,
additionalRedirectUrls,
dashboardSentryIssueId,
dashboardLogs,
dashboardStudioVersion,
}: sendSupportTicketVariables) {
const { data, error } = await post('/platform/feedback/send', {
body: {
@@ -54,6 +58,8 @@ export async function sendSupportTicket({
browserInformation,
allowSupportAccess,
dashboardSentryIssueId,
dashboardLogs,
dashboardStudioVersion,
},
})