Files
supabase/apps/studio/components/interfaces/Functions/EdgeFunctionsListItem.tsx
T
Joshen Lim 09a1f431bc Mass update for all lucide icon imports to use the component with Icon suffix (#51659)
## Context

This is largely just a mechanical change here:
Opting to use lucide icons with the `Icon` suffix across all icon
imports within `studio`
- so that it’s clearer which components are icons + reduces the
possibility of name collision with our own components
- e.g <User /> vs <UserIcon />

Also added a eslint rule for this:
<img width="1069" height="167" alt="image"
src="https://github.com/user-attachments/assets/7f36f443-5ca2-4bf6-8d22-86e774513bfc"
/>

And updated `AGENTS.md` as such:
```
Icons come from `lucide-react`, always imported with the `Icon` suffix (`import { CheckIcon, Trash2Icon } from 'lucide-react'`) — never the bare names (`Check`) or `Check as CheckIcon` aliases (lint-enforced).
```
2026-10-14 12:56:55 +02:00

175 lines
6.2 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 { CheckIcon, CopyIcon } 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">
<CheckIcon size={14} strokeWidth={3} />
</div>
) : (
<div className="relative">
<div className="block">
<CopyIcon 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>
)
}