mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 01:45:10 +03:00
Closes DOCS-1281 ## Problem Two defects in the "Related error codes" list, both from the page diverging from what `Troubleshooting.utils.ts` already does. * **Empty pills.** `formatError` returns an empty string when an error has neither an HTTP status code nor a code. The page renders the pill anyway, giving a link with no text whose `href` ends in `errorCodes=` with no value. So it is both an unnamed link and a pill filtering on nothing. * **Duplicate pills.** The same formatted code renders once per underlying error object, so one entry shows seven identical "500 unexpected_failure" pills. Measured on production, across the 59 entries that render the section: | | Count | | -- | -- | | Entries with an empty pill | 23 | | Empty pills | 33 | | Entries with duplicate pills | 4 | | Redundant pills | 9 | The guard also evaluated to `0` rather than `false` for an empty array, which React renders as a literal "0". ## Solution * Derive the formatted codes once, drop the empties, and dedupe. An entry whose every code formats empty no longer renders a heading and rule with nothing under them. * Call `formatError` once per code instead of twice per pill, and key on the code now that codes are unique. * Fix the same `0`-rendering guard on the keywords section. `Troubleshooting.utils.ts` already filters on `error?.http_status_code || error?.code` at lines 69 and 150, and already dedupes by formatted code at lines 72 to 79. This brings the page in line with the sidebar and filter list rather than introducing a new pattern. `formatError` itself is unchanged. It also produces grouping and sort keys in `Troubleshooting.utils.ts` and `Troubleshooting.ui.tsx`, so changing its return contract would reach well beyond this fix. ## Manual testing Compare each page against production, which still shows both defects. 1. Open [dashboard-errors-when-managing-users on production](https://supabase.com/docs/guides/troubleshooting/dashboard-errors-when-managing-users-N1ls4A). It shows 8 pills: seven identical "500 unexpected_failure" and one empty. 2. Open [the same page on the preview](https://docs-git-docs-troubleshooting-empty-error-pills-supabase.vercel.app/docs/guides/troubleshooting/dashboard-errors-when-managing-users-N1ls4A). One "500 unexpected_failure" pill remains. 3. Open [prisma-error-management on production](https://supabase.com/docs/guides/troubleshooting/prisma-error-management-Cm5P_o). It shows 6 empty pills. 4. Open [the same page on the preview](https://docs-git-docs-troubleshooting-empty-error-pills-supabase.vercel.app/docs/guides/troubleshooting/prisma-error-management-Cm5P_o). The section is gone, because every code on that entry formats empty. 5. Run axe on either preview page. `link-name` reports zero elements. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved troubleshooting displays by formatting and deduplicating error values. * Removed empty or invalid error entries from the rendered results. * Related error-code links now appear only when valid error codes are available. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
133 lines
5.6 KiB
TypeScript
133 lines
5.6 KiB
TypeScript
import { Github } from 'lucide-react'
|
|
import Link from 'next/link'
|
|
import { cn } from 'ui'
|
|
import Breadcrumbs from '~/components/Breadcrumbs'
|
|
import { Feedback } from '~/components/Feedback'
|
|
import { SidebarSkeleton } from '~/layouts/MainSkeleton'
|
|
import { MDXRemoteBase } from './MdxBase'
|
|
import { getTroubleshootingUpdatedDates, type ITroubleshootingEntry } from './Troubleshooting.utils'
|
|
import { formatError, serializeTroubleshootingSearchParams } from './Troubleshooting.utils.shared'
|
|
|
|
export default async function TroubleshootingPage({ entry }: { entry: ITroubleshootingEntry }) {
|
|
const dateUpdated = entry.data.database_id.startsWith('pseudo-')
|
|
? new Date()
|
|
: (await getTroubleshootingUpdatedDates()).get(entry.data.database_id)
|
|
const errorCodes = [...new Set(entry.data.errors?.map(formatError).filter(Boolean) ?? [])]
|
|
|
|
return (
|
|
<SidebarSkeleton
|
|
hideSideNav
|
|
className="@container/troubleshooting-entry-layout w-full max-w-(--breakpoint-lg) mx-auto lg:py-8 lg:px-5"
|
|
>
|
|
<div className="px-5 py-8 lg:px-0 lg:py-0">
|
|
<Breadcrumbs minLength={1} forceDisplayOnMobile />
|
|
<article
|
|
id="sb-docs-troubleshooting-main-article"
|
|
data-testid="sb-docs-troubleshooting-main-article"
|
|
className="prose max-w-none mt-4"
|
|
>
|
|
<h1>{entry.data.title}</h1>
|
|
{dateUpdated && (
|
|
<p className="text-sm text-foreground-lighter">
|
|
Last edited: {dateUpdated.toLocaleDateString()}
|
|
</p>
|
|
)}
|
|
<hr className="my-7" aria-hidden />
|
|
<div className="grid gap-10 @3xl/troubleshooting-entry-layout:grid-cols-[1fr_250px]">
|
|
<div className="min-w-0">
|
|
<MDXRemoteBase source={entry.content} />
|
|
</div>
|
|
<aside aria-labelledby="heading--metadata" className="not-prose mt-5">
|
|
<h2
|
|
id="heading--metadata"
|
|
className="text-foreground-lighter uppercase text-sm tracking-wide @3xl/troubleshooting-entry-layout:sr-only"
|
|
>
|
|
Metadata
|
|
</h2>
|
|
<hr className="my-6 @3xl/troubleshooting-entry-layout:hidden" aria-hidden />
|
|
{entry.data.topics?.length > 0 && (
|
|
<>
|
|
<h3 className="text-sm text-foreground-lighter mb-3">Products</h3>
|
|
<div className="flex flex-wrap gap-0.5">
|
|
{entry.data.topics.map((topic) => (
|
|
<Link
|
|
key={topic}
|
|
href={`/guides/troubleshooting${serializeTroubleshootingSearchParams({ products: [topic] })}`}
|
|
>
|
|
<PillTag className="hover:bg-200 focus-visible:bg-foreground-muted hover:border-control focus-visible:border-control transition-colors">
|
|
{topic[0].toUpperCase() + topic.slice(1)}
|
|
</PillTag>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
<hr className="my-6" aria-hidden />
|
|
</>
|
|
)}
|
|
{errorCodes.length > 0 && (
|
|
<>
|
|
<h3 className="text-sm text-foreground-lighter mb-3">Related error codes</h3>
|
|
<div className="flex flex-wrap gap-0.5">
|
|
{errorCodes.map((errorCode) => (
|
|
<Link
|
|
key={errorCode}
|
|
href={`/guides/troubleshooting${serializeTroubleshootingSearchParams({ errorCodes: [errorCode] })}`}
|
|
>
|
|
<PillTag className="hover:bg-200 focus-visible:bg-foreground-muted hover:border-control focus-visible:border-control transition-colors">
|
|
{errorCode}
|
|
</PillTag>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
<hr className="my-6" aria-hidden />
|
|
</>
|
|
)}
|
|
{!!entry.data.keywords?.length && (
|
|
<>
|
|
<h3 className="text-sm text-foreground-lighter mb-3">Keywords</h3>
|
|
<div className="flex flex-wrap gap-0.5">
|
|
{entry.data.keywords.map((keyword) => (
|
|
<Link
|
|
key={keyword}
|
|
href={`/guides/troubleshooting${serializeTroubleshootingSearchParams({ tags: [keyword] })}`}
|
|
>
|
|
<PillTag className="hover:bg-200 focus-visible:bg-foreground-muted hover:border-control focus-visible:border-control transition-colors">
|
|
{keyword}
|
|
</PillTag>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
<hr className="my-6" aria-hidden />
|
|
</>
|
|
)}
|
|
<Feedback className="px-0 mb-6 lg:mb-8" />
|
|
{entry.data.github_url && (
|
|
<>
|
|
<hr className="my-6" aria-hidden />
|
|
<Link
|
|
target="_blank"
|
|
href={entry.data.github_url}
|
|
className="flex items-center gap-2 text-sm text-foreground-lighter"
|
|
>
|
|
<Github size={14} />
|
|
View discussion on GitHub
|
|
</Link>
|
|
</>
|
|
)}
|
|
</aside>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
</SidebarSkeleton>
|
|
)
|
|
}
|
|
|
|
function PillTag({ children, className }: { children: React.ReactNode; className?: string }) {
|
|
return (
|
|
<span
|
|
className={cn('px-2 py-px border rounded-full inline-flex items-center text-xs', className)}
|
|
>
|
|
{children}
|
|
</span>
|
|
)
|
|
}
|