Revert "feat(docs): route docs page feedback comments to Postgres" (#47027)

Reverts supabase/supabase#46941

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **New Features**
* Feedback is now automatically routed to the appropriate documentation
team based on the section being viewed.

* **Improvements**
* Streamlined feedback submission process—votes and comments are now
collected more efficiently in a single submission.
* Enhanced feedback data handling and organization for better team
collaboration.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Chris Chinchilla authored and GitHub committed 2026-06-17 11:49:45 +00:00
1 parent 8b05c769fb
commit c9baed829f
6 files changed
+114 -100

No files matched your search

+25 -37
View File
@@ -2,8 +2,9 @@
import { createClient } from '@supabase/supabase-js'
import { IS_PLATFORM } from '~/lib/constants'
import { useSendFeedbackMutation } from '~/lib/fetch/feedback'
import { useSendTelemetryEvent } from '~/lib/telemetry'
import { gotrueClient, useConstant, useIsLoggedIn, type Database } from 'common'
import { useConstant, useIsLoggedIn, type Database } from 'common'
import { Check, MessageSquareQuote, X } from 'lucide-react'
import { usePathname } from 'next/navigation'
import {
@@ -16,7 +17,7 @@ import {
} from 'react'
import { Button, cn } from 'ui'
import { getSanitizedTabParams, updateDocsFeedbackComment } from './Feedback.utils'
import { getLinearTeam, getSanitizedTabParams } from './Feedback.utils'
import { FeedbackModal, type FeedbackFields } from './FeedbackModal'
const FeedbackButton = forwardRef<
@@ -77,20 +78,15 @@ function Feedback({ className }: { className?: string }) {
const pathname = usePathname() ?? ''
const sendTelemetryEvent = useSendTelemetryEvent()
const { mutate: sendFeedbackComment } = useSendFeedbackMutation()
const supabase = useConstant(() =>
IS_PLATFORM
? createClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
accessToken: async () =>
(await gotrueClient.getSession()).data.session?.access_token ?? null,
}
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
: undefined
)
const isLoggedIn = useIsLoggedIn()
const feedbackInsertRef = useRef<Promise<number | null> | null>(null)
const unanswered = state.type === 'unanswered'
const isYes = 'response' in state && state.response === 'yes'
@@ -98,26 +94,17 @@ function Feedback({ className }: { className?: string }) {
const showYes = unanswered || isYes
const showNo = unanswered || isNo
// Returns the new row's id for logged-in users (who can read their own row
// back) so an optional comment can be attached afterwards. Anonymous votes are
// insert-only and resolve to null.
async function sendFeedbackVote(response: Response): Promise<number | null> {
if (!supabase) return null
async function sendFeedbackVote(response: Response) {
if (!supabase) return
const row = { vote: response, page: pathname, metadata: { query: getSanitizedTabParams() } }
if (isLoggedIn) {
const { data, error } = await supabase.from('feedback').insert(row).select('id').single()
if (error) {
console.error(error)
return null
}
return data.id
} else {
const { error } = await supabase.from('feedback').insert(row)
if (error) console.error(error)
return null
}
const { error } = await supabase.from('feedback').insert({
vote: response,
page: pathname,
metadata: {
query: getSanitizedTabParams(),
},
})
if (error) console.error(error)
}
function handleVote(response: Response) {
@@ -125,7 +112,7 @@ function Feedback({ className }: { className?: string }) {
action: 'docs_feedback_clicked',
properties: { response },
})
feedbackInsertRef.current = sendFeedbackVote(response)
sendFeedbackVote(response)
dispatch({ event: 'VOTED', response })
// Focus so screen reader users are aware of the new element
setTimeout(() => {
@@ -141,14 +128,15 @@ function Feedback({ className }: { className?: string }) {
}, 100)
}
async function handleSubmit({ comment, title }: FeedbackFields) {
// Wait for the vote insert so the comment update can't run before the row
// (and its id) exists.
const id = await feedbackInsertRef.current
if (supabase && id !== null) {
const { error } = await updateDocsFeedbackComment(supabase, { id, title, comment })
if (error) console.error(error)
}
async function handleSubmit({ page, comment, title }: FeedbackFields) {
sendFeedbackComment({
message: comment,
pathname: page,
title,
// @ts-expect-error -- can't click this button without having a state.response
isHelpful: state.response === 'yes',
team: getLinearTeam(pathname),
})
setModalOpen(false)
refocusButton()
}
@@ -1,25 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import { updateDocsFeedbackComment } from './Feedback.utils'
describe('updateDocsFeedbackComment', () => {
it('updates the feedback row by id with the comment', async () => {
const eq = vi.fn().mockResolvedValue({ error: null })
const update = vi.fn(() => ({ eq }))
const from = vi.fn(() => ({ update }))
const supabase = { from } as any
await updateDocsFeedbackComment(supabase, {
id: 42,
title: 'Great explanation',
comment: 'This page was really helpful.',
})
expect(from).toHaveBeenCalledWith('feedback')
expect(update).toHaveBeenCalledWith({
title: 'Great explanation',
comment: 'This page was really helpful.',
})
expect(eq).toHaveBeenCalledWith('id', 42)
})
})
+39 -12
View File
@@ -1,17 +1,44 @@
import type { SupabaseClient } from '@supabase/supabase-js'
import type { Database } from 'common'
import { pick } from 'lodash-es'
type FeedbackComment = {
id: number
title: string
comment: string
}
/**
* Gets the Notion team to send feedback to based on the pathname.
*/
const getLinearTeam = (pathname: string) => {
const DEFAULT_TEAM = 'Docs'
const updateDocsFeedbackComment = (
supabase: SupabaseClient<Database>,
{ id, title, comment }: FeedbackComment
) => supabase.from('feedback').update({ title, comment }).eq('id', id)
// Pathname has format `/guides/(team)/**`
const pathParts = pathname.split('/')
if (pathParts[1] !== 'guides' || !pathParts[2]) return DEFAULT_TEAM
switch (pathParts[2]) {
case 'database':
return 'Postgres'
case 'auth':
return 'Auth'
case 'storage':
return 'Storage'
case 'functions':
return 'Functions'
case 'realtime':
return 'Realtime'
case 'ai':
return 'AI'
case 'local-development':
case 'self-hosting':
case 'deployment':
return 'Dev Workflows'
case 'integrations':
return 'API'
case 'security':
return 'Security'
case 'platform':
case 'monitoring-troubleshooting':
return 'Infra'
default:
return DEFAULT_TEAM
}
}
/**
* Gets the tab selection state from the URL search params.
@@ -26,4 +53,4 @@ const getSanitizedTabParams = () => {
return pick(Object.fromEntries(searchParams.entries()), queryGroups)
}
export { updateDocsFeedbackComment, getSanitizedTabParams }
export { getLinearTeam, getSanitizedTabParams }
+50
View File
@@ -0,0 +1,50 @@
import { type UseMutationOptions, useMutation } from '@tanstack/react-query'
import { type ResponseError } from '~/types/fetch'
import { post } from './fetchWrappers'
type SendFeedbackVariables = {
title: string
message: string
isHelpful: boolean
pathname?: string
team?: string
}
export async function sendFeedback({
message,
pathname,
title,
isHelpful,
team,
}: SendFeedbackVariables) {
const { data, error } = await post('/platform/feedback/docs', {
body: {
page: pathname ?? '',
isHelpful,
title,
feedback: message,
team,
},
})
if (error) throw Error(`Couldn't send feedback`, { cause: error })
return data
}
type SendFeedbackData = Awaited<ReturnType<typeof sendFeedback>>
export const useSendFeedbackMutation = (
options: Omit<
UseMutationOptions<SendFeedbackData, ResponseError, SendFeedbackVariables>,
'mutationFn'
> = {}
) => {
return useMutation<SendFeedbackData, ResponseError, SendFeedbackVariables>({
...options,
mutationFn: (vars) => sendFeedback(vars),
onError: (error, vars, ctx) => {
console.error(error)
options.onError?.(error, vars, ctx)
},
})
}
-9
View File
@@ -129,33 +129,24 @@ export type Database = {
Tables: {
feedback: {
Row: {
comment: string | null
date_created: string
id: number
metadata: Json | null
page: string
title: string | null
user_id: string | null
vote: Database['public']['Enums']['feedback_vote']
}
Insert: {
comment?: string | null
date_created?: string
id?: never
metadata?: Json | null
page: string
title?: string | null
user_id?: string | null
vote: Database['public']['Enums']['feedback_vote']
}
Update: {
comment?: string | null
date_created?: string
id?: never
metadata?: Json | null
page?: string
title?: string | null
user_id?: string | null
vote?: Database['public']['Enums']['feedback_vote']
}
Relationships: []
@@ -1,17 +0,0 @@
alter table feedback
add column user_id uuid default auth.uid(),
add column title text,
add column comment text;
create policy "Users can read their own feedback"
on feedback
as permissive for select
to authenticated
using ((select auth.uid()) = user_id);
create policy "Users can update their own feedback"
on feedback
as permissive for update
to authenticated
using ((select auth.uid()) = user_id)
with check ((select auth.uid()) = user_id);