From c9baed829f9790ea17e7b9ffc200f5c6d97b64f6 Mon Sep 17 00:00:00 2001 From: Chris Chinchilla Date: Wed, 17 Jun 2026 13:49:45 +0200 Subject: [PATCH] Revert "feat(docs): route docs page feedback comments to Postgres" (#47027) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverts supabase/supabase#46941 ## 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. --- apps/docs/components/Feedback/Feedback.tsx | 62 ++++++++----------- .../Feedback/Feedback.utils.test.ts | 25 -------- .../components/Feedback/Feedback.utils.ts | 51 +++++++++++---- apps/docs/lib/fetch/feedback.ts | 50 +++++++++++++++ packages/common/database-types.ts | 9 --- ...260616074454_add_docs_feedback_comment.sql | 17 ----- 6 files changed, 114 insertions(+), 100 deletions(-) delete mode 100644 apps/docs/components/Feedback/Feedback.utils.test.ts create mode 100644 apps/docs/lib/fetch/feedback.ts delete mode 100644 supabase/migrations/20260616074454_add_docs_feedback_comment.sql diff --git a/apps/docs/components/Feedback/Feedback.tsx b/apps/docs/components/Feedback/Feedback.tsx index fc72544687e..ba4afd00ace 100644 --- a/apps/docs/components/Feedback/Feedback.tsx +++ b/apps/docs/components/Feedback/Feedback.tsx @@ -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( 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 | 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 { - 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() } diff --git a/apps/docs/components/Feedback/Feedback.utils.test.ts b/apps/docs/components/Feedback/Feedback.utils.test.ts deleted file mode 100644 index ab1a600894a..00000000000 --- a/apps/docs/components/Feedback/Feedback.utils.test.ts +++ /dev/null @@ -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) - }) -}) diff --git a/apps/docs/components/Feedback/Feedback.utils.ts b/apps/docs/components/Feedback/Feedback.utils.ts index 16b6ebf18ce..c93ebb55715 100644 --- a/apps/docs/components/Feedback/Feedback.utils.ts +++ b/apps/docs/components/Feedback/Feedback.utils.ts @@ -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, - { 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 } diff --git a/apps/docs/lib/fetch/feedback.ts b/apps/docs/lib/fetch/feedback.ts new file mode 100644 index 00000000000..8bafd80ff59 --- /dev/null +++ b/apps/docs/lib/fetch/feedback.ts @@ -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> + +export const useSendFeedbackMutation = ( + options: Omit< + UseMutationOptions, + 'mutationFn' + > = {} +) => { + return useMutation({ + ...options, + mutationFn: (vars) => sendFeedback(vars), + onError: (error, vars, ctx) => { + console.error(error) + options.onError?.(error, vars, ctx) + }, + }) +} diff --git a/packages/common/database-types.ts b/packages/common/database-types.ts index cb90d269de9..1fe0d208dc7 100644 --- a/packages/common/database-types.ts +++ b/packages/common/database-types.ts @@ -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: [] diff --git a/supabase/migrations/20260616074454_add_docs_feedback_comment.sql b/supabase/migrations/20260616074454_add_docs_feedback_comment.sql deleted file mode 100644 index f6934732170..00000000000 --- a/supabase/migrations/20260616074454_add_docs_feedback_comment.sql +++ /dev/null @@ -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);