Files
supabase/apps/studio/components/interfaces/Functions/EdgeFunctionsListItem.tsx
T
Danny White ec53175b8a refactor(ui): rename text-brand to text-primary (#50564)
## What kind of change does this PR introduce?

Refactor. Follow-up to #49871.

## What is the current behavior?

Branded (green) text still uses the `text-brand` classname while the
colour comes from `--primary`.

## What is the new behavior?

**Rename-only:** `text-brand` → `text-primary` across callsites and
docs. Leftover `bg-brand` / `border-brand` alias to `brand-default`. No
intentional colour changes in this PR.

This better matches how we treat our green in other components and
props, like `variant="primary"` for green buttons.

## To test

On light mode: smoke-test that branded text still looks like #49871
(readable green, not the bright fill).

-
[Homepage](https://zone-www-dot-com-git-dnywh-depr-316-text-brand-de2380-supabase.vercel.app/):
“Scale to millions” uses `text-primary`
- [Docs
homepage](https://docs-git-dnywh-depr-316-text-brand-to-primary-supabase.vercel.app/docs):
branded links like “More on self-hosting” are still readable
- [Typography
docs](https://design-system-git-dnywh-depr-316-text-brand-to-primary-supabase.vercel.app/design-system/docs/typography):
documents `text-primary`
- [Colour
usage](https://design-system-git-dnywh-depr-316-text-brand-to-primary-supabase.vercel.app/design-system/docs/color-usage):
`text-primary` is visibly darker than `bg-brand-default` _on light mode_
- [Studio auth
providers](https://studio-staging-git-dnywh-depr-316-text-brand-to-60fa6c-supabase.vercel.app/dashboard/project/_/auth/providers):
enabled provider badge text readable; status dot stays bright green

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

* **Style**
* Updated branded text, link hover states, icons, badges, indicators,
and highlighted content across the Design System, Docs, Studio, Learn,
UI Library, and marketing experiences to use the primary theme color.
* Updated syntax highlighting and table-of-contents styling for
consistent primary-color presentation.
  * Refined brand color fallback behavior for bright fills and borders.

* **Documentation**
* Updated color-usage and typography guidance to recommend the primary
text utility.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-22 16:19:03 +10:00

175 lines
6.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { IS_PLATFORM, useFlag } from 'common'
import { useParams } from 'common/hooks'
import dayjs from 'dayjs'
import { Check, Copy } from 'lucide-react'
import { useRouter } from 'next/router'
import { useMemo, useState, type MouseEvent } from 'react'
import { cn, copyToClipboard, TableCell, TableRow } from 'ui'
import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
import { TimestampInfo } from 'ui-patterns/TimestampInfo'
import { formatErrorRate } from './EdgeFunctionsListItem.utils'
import { useProjectApiUrl } from '@/data/config/project-endpoint-query'
import { useEdgeFunctionsLastHourStatsQuery } from '@/data/edge-functions/edge-functions-last-hour-stats-query'
import {
useEdgeFunctionsQuery,
type EdgeFunctionsResponse,
} from '@/data/edge-functions/edge-functions-query'
import { normalizeFunctionIds } from '@/data/edge-functions/keys'
import { createNavigationHandler } from '@/lib/navigation'
interface EdgeFunctionsListItemProps {
function: EdgeFunctionsResponse
}
export const EdgeFunctionsListItem = ({ function: item }: EdgeFunctionsListItemProps) => {
const router = useRouter()
const { ref } = useParams()
const [isCopied, setIsCopied] = useState(false)
const showLastHourStats = useFlag('edgeFunctionsRequestMetrics')
const { data: endpoint } = useProjectApiUrl({ projectRef: ref })
const functionUrl = `${endpoint}/functions/v1/${item.slug}`
const handleNavigation = createNavigationHandler(
`/project/${ref}/functions/${item.slug}${IS_PLATFORM ? '' : `/code`}`,
router
)
const { data: functions } = useEdgeFunctionsQuery({ projectRef: ref })
const functionIds = useMemo(() => {
if (!showLastHourStats || !functions) return []
return normalizeFunctionIds(functions.map((item) => item.id))
}, [functions, showLastHourStats])
// [Joshen] We may be paginating the edge functions query in the future
// So this will eventually need to be a list of visibleFunctionIds instead + debounced
const {
data: lastHourStatsAll,
isPending: isStatsPending,
isError: isStatsError,
} = useEdgeFunctionsLastHourStatsQuery(
{ projectRef: ref, functionIds },
{ enabled: showLastHourStats }
)
const lastHourStats = lastHourStatsAll?.[item.id]
return (
<TableRow
key={item.id}
onClick={handleNavigation}
onAuxClick={handleNavigation}
onKeyDown={handleNavigation}
tabIndex={0}
className="cursor-pointer focus-inset"
>
<TableCell>
<p className="text-sm text-foreground whitespace-nowrap py-2">{item.name}</p>
</TableCell>
<TableCell>
<div className="text-xs text-foreground-light flex gap-2 items-center truncate">
<p title={functionUrl} className="font-mono truncate hidden md:inline max-w-120">
{functionUrl}
</p>
<button
type="button"
tabIndex={0}
className="text-foreground-lighter hover:text-foreground transition"
onClick={(event: MouseEvent<HTMLButtonElement>) => {
function onCopy(value: string) {
setIsCopied(true)
copyToClipboard(value)
setTimeout(() => setIsCopied(false), 3000)
}
event.stopPropagation()
onCopy(functionUrl)
}}
>
{isCopied ? (
<div className="text-primary">
<Check size={14} strokeWidth={3} />
</div>
) : (
<div className="relative">
<div className="block">
<Copy size={14} strokeWidth={1.5} />
</div>
</div>
)}
</button>
</div>
</TableCell>
<TableCell className="hidden 2xl:table-cell whitespace-nowrap">
{item.created_at ? (
<TimestampInfo
className="text-sm text-foreground-light whitespace-nowrap"
utcTimestamp={item.created_at}
label={dayjs(item.created_at).fromNow()}
/>
) : (
<span className="text-sm text-foreground-light">–</span>
)}
</TableCell>
<TableCell className="lg:table-cell">
{item.updated_at ? (
<TimestampInfo
className="text-sm text-foreground-light whitespace-nowrap"
utcTimestamp={item.updated_at}
label={dayjs(item.updated_at).fromNow()}
/>
) : (
<span className="text-sm text-foreground-light">–</span>
)}
</TableCell>
{showLastHourStats && (
<>
<TableCell className="lg:table-cell whitespace-nowrap">
{isStatsPending ? (
<ShimmeringLoader className="w-12" />
) : isStatsError ? (
<p className="text-foreground-lighter" title="Failed to load stats">
-
</p>
) : (
<p className="text-foreground-light">
{lastHourStats !== undefined ? lastHourStats.requestsCount.toLocaleString() : '-'}
</p>
)}
</TableCell>
<TableCell className="lg:table-cell whitespace-nowrap">
{isStatsPending ? (
<ShimmeringLoader className="w-12" />
) : isStatsError ? (
<p className="text-foreground-lighter" title="Failed to load stats">
-
</p>
) : lastHourStats !== undefined ? (
<span
className={cn(
'text-sm',
lastHourStats.errorRate >= 1
? 'text-destructive'
: lastHourStats.errorRate > 0.1
? 'text-warning'
: 'text-foreground-light'
)}
>
{formatErrorRate(lastHourStats.errorRate)}
</span>
) : (
<p className="text-foreground-lighter">-</p>
)}
</TableCell>
</>
)}
<TableCell className="hidden 2xl:table-cell">
<p className="text-foreground-light">{item.version}</p>
<button tabIndex={-1} className="sr-only">
Go to function details
</button>
</TableCell>
</TableRow>
)
}