Files
Ali Waseem f3094a29ce Remove cloud provider text from project cards (#50754)
Project cards and the project table showed the raw cloud provider
(`AWS`, `AWS_K8S`) alongside the region, which is an internal
implementation detail. Both now show the region only — matching the
table's existing "Region" column header.

Fixes FE-4441

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

## Summary by CodeRabbit

* **Updates**
  * Project cards now display the project’s region directly.
* Project tables show only the region in the region column, with “N/A”
when unavailable.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-23 07:40:48 -06:00

207 lines
7.6 KiB
TypeScript
Raw Permalink 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 { Check, Copy, Github, MoreVertical, Settings } from 'lucide-react'
import { useRouter } from 'next/router'
import { useState } from 'react'
import InlineSVG from 'react-inlinesvg'
import { toast } from 'sonner'
import {
Button,
copyToClipboard,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
TableCell,
TableRow,
Tooltip,
TooltipContent,
TooltipTrigger,
} from 'ui'
import { TimestampInfo } from 'ui-patterns/TimestampInfo'
import { inferProjectStatus } from './ProjectCard.utils'
import { ProjectCardStatus } from './ProjectCardStatus'
import { ComputeBadgeWrapper } from '@/components/ui/ComputeBadgeWrapper'
import PartnerIcon from '@/components/ui/PartnerIcon'
import type { IntegrationProjectConnection } from '@/data/integrations/integrations.types'
import { getManagedByFromOrganizationPartner } from '@/data/organizations/managed-by-utils'
import { getComputeSize, OrgProject } from '@/data/projects/org-projects-infinite-query'
import type { ResourceWarning } from '@/data/usage/resource-warnings-query'
import { BASE_PATH } from '@/lib/constants'
import { MANAGED_BY } from '@/lib/constants/infrastructure'
import { createNavigationHandler } from '@/lib/navigation'
import type { Organization } from '@/types'
export interface ProjectTableRowProps {
project: OrgProject
organization?: Organization
rewriteHref?: string
githubIntegration?: IntegrationProjectConnection
vercelIntegration?: IntegrationProjectConnection
resourceWarnings?: ResourceWarning
}
export const ProjectTableRow = ({
project,
organization,
rewriteHref,
githubIntegration,
vercelIntegration,
resourceWarnings,
}: ProjectTableRowProps) => {
const router = useRouter()
const { name, ref: projectRef } = project
const projectStatus = inferProjectStatus(project.status)
const [isCopied, setIsCopied] = useState(false)
const url = rewriteHref ?? `/project/${project.ref}`
const isGithubIntegrated = githubIntegration !== undefined
const isVercelIntegrated = vercelIntegration !== undefined
const githubRepository = githubIntegration?.metadata.name ?? undefined
const projectManagedBy = getManagedByFromOrganizationPartner(
undefined,
project.integration_source
)
const hasPartnerIcon = projectManagedBy !== MANAGED_BY.SUPABASE
const handleNavigation = createNavigationHandler(url, router)
const handleCopyProjectRef = (e: React.SyntheticEvent) => {
e.stopPropagation()
copyToClipboard(projectRef)
setIsCopied(true)
toast.success('Copied project ID to clipboard')
setTimeout(() => setIsCopied(false), 2000)
}
return (
<>
<TableRow
className="cursor-pointer hover:bg-surface-200 focus-inset"
onClick={handleNavigation}
onAuxClick={handleNavigation}
onKeyDown={handleNavigation}
tabIndex={0}
>
<TableCell>
<div className="flex flex-col gap-y-2">
<div>
<h2 className="text-sm">{name}</h2>
<Tooltip>
<TooltipTrigger asChild>
<button
tabIndex={0}
onClick={handleCopyProjectRef}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault()
handleCopyProjectRef(e)
}
}}
className="inline-flex items-center gap-x-1 cursor-pointer border border-transparent border-dashed rounded-sm transition-colors hover:bg-surface-100 hover:border hover:border-strong group font-mono text-xs text-foreground-lighter hover:text-foreground-light px-1 -ml-1"
>
{projectRef}
{isCopied ? (
<Check size={12} strokeWidth={1.25} className="text-primary" />
) : (
<Copy
size={12}
strokeWidth={1.25}
className="opacity-0 group-hover:opacity-100 transition-opacity"
/>
)}
</button>
</TooltipTrigger>
<TooltipContent>Copy project reference</TooltipContent>
</Tooltip>
</div>
{(isGithubIntegrated || isVercelIntegrated || hasPartnerIcon) && (
<div className="flex items-center gap-x-1.5">
{isVercelIntegrated && (
<div className="bg-surface-100 w-5 h-5 p-1 border border-strong rounded-md flex items-center text-black dark:text-white">
<InlineSVG
src={`${BASE_PATH}/img/icons/vercel-icon.svg`}
title="Vercel Icon"
className="w-3"
/>
</div>
)}
<PartnerIcon organization={{ managed_by: projectManagedBy }} />
{isGithubIntegrated && (
<div className="bg-surface-100 flex items-center gap-x-0.5 h-5 pr-1 border border-strong rounded-md">
<div className="w-5 h-5 p-1 flex items-center">
<Github size={12} strokeWidth={1.5} />
</div>
{githubRepository && (
<p className="text-xs text-foreground-light truncate">{githubRepository}</p>
)}
</div>
)}
</div>
)}
</div>
</TableCell>
<TableCell>
<ProjectCardStatus
projectStatus={projectStatus}
resourceWarnings={resourceWarnings}
renderMode="badge"
/>
</TableCell>
<TableCell>
<div className="w-fit">
{project.status !== 'INACTIVE' ? (
<ComputeBadgeWrapper
slug={organization?.slug}
projectRef={project.ref}
cloudProvider={project.cloud_provider}
computeSize={getComputeSize(project)}
resourceWarnings={resourceWarnings}
/>
) : (
<span className="text-xs text-foreground-muted">–</span>
)}
</div>
</TableCell>
<TableCell>
<span className="lowercase text-sm text-foreground-light">{project.region || 'N/A'}</span>
</TableCell>
<TableCell>
{project.inserted_at ? (
<TimestampInfo
className="text-sm text-foreground-light"
utcTimestamp={project.inserted_at}
/>
) : (
<span className="text-sm text-foreground-light">N/A</span>
)}
</TableCell>
<TableCell className="text-right">
<div onClick={(e) => e.stopPropagation()}>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
icon={<MoreVertical />}
aria-label={`Project ${name} actions`}
className="w-7"
onClick={(e) => e.stopPropagation()}
/>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48">
<DropdownMenuItem
className="gap-x-2"
onClick={(e) => {
e.stopPropagation()
router.push(`/project/${projectRef}/settings/general`)
}}
>
<Settings size={14} />
<span>Settings</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</TableCell>
</TableRow>
</>
)
}