| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- 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,
- } from 'ui'
- import { TimestampInfo } from 'ui-patterns'
- 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.BRIVEN
- 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 inset-focus"
- onClick={handleNavigation}
- onAuxClick={handleNavigation}
- onKeyDown={handleNavigation}
- tabIndex={0}
- >
- <TableCell>
- <div className="flex flex-col gap-y-2">
- <div>
- <h5 className="text-sm">{name}</h5>
- <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-brand" />
- ) : (
- <Copy
- size={12}
- strokeWidth={1.25}
- className="opacity-0 group-hover:opacity-100 transition-opacity"
- />
- )}
- </button>
- </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.cloud_provider} | {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
- type="default"
- icon={<MoreVertical />}
- aria-label="More 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>
- </>
- )
- }
|