ProjectCard.tsx 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import { Copy, Github, MoreVertical, Settings } from 'lucide-react'
  2. import { useRouter } from 'next/router'
  3. import InlineSVG from 'react-inlinesvg'
  4. import { toast } from 'sonner'
  5. import {
  6. Button,
  7. copyToClipboard,
  8. DropdownMenu,
  9. DropdownMenuContent,
  10. DropdownMenuItem,
  11. DropdownMenuTrigger,
  12. } from 'ui'
  13. import { inferProjectStatus } from './ProjectCard.utils'
  14. import { ProjectCardStatus } from './ProjectCardStatus'
  15. import CardButton from '@/components/ui/CardButton'
  16. import { ComputeBadgeWrapper } from '@/components/ui/ComputeBadgeWrapper'
  17. import PartnerIcon from '@/components/ui/PartnerIcon'
  18. import type { IntegrationProjectConnection } from '@/data/integrations/integrations.types'
  19. import { getManagedByFromOrganizationPartner } from '@/data/organizations/managed-by-utils'
  20. import { ProjectIndexPageLink } from '@/data/prefetchers/project.$ref'
  21. import { getComputeSize, OrgProject } from '@/data/projects/org-projects-infinite-query'
  22. import type { ResourceWarning } from '@/data/usage/resource-warnings-query'
  23. import { useCustomContent } from '@/hooks/custom-content/useCustomContent'
  24. import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
  25. import { BASE_PATH } from '@/lib/constants'
  26. import type { Organization } from '@/types'
  27. export interface ProjectCardProps {
  28. slug?: string
  29. project: OrgProject
  30. organization?: Organization
  31. rewriteHref?: string
  32. githubIntegration?: IntegrationProjectConnection
  33. vercelIntegration?: IntegrationProjectConnection
  34. resourceWarnings?: ResourceWarning
  35. }
  36. export const ProjectCard = ({
  37. slug,
  38. project,
  39. rewriteHref,
  40. githubIntegration,
  41. vercelIntegration,
  42. resourceWarnings,
  43. }: ProjectCardProps) => {
  44. const router = useRouter()
  45. const { name, ref: projectRef } = project
  46. const { infraAwsNimbusLabel } = useCustomContent(['infra:aws_nimbus_label'])
  47. const providerLabel =
  48. project.cloud_provider === 'AWS_NIMBUS' ? infraAwsNimbusLabel : project.cloud_provider
  49. const desc = `${providerLabel} | ${project.region}`
  50. const { projectHomepageShowInstanceSize } = useIsFeatureEnabled([
  51. 'project_homepage:show_instance_size',
  52. ])
  53. const isGithubIntegrated = githubIntegration !== undefined
  54. const isVercelIntegrated = vercelIntegration !== undefined
  55. const githubRepository = githubIntegration?.metadata.name ?? undefined
  56. const projectManagedBy = getManagedByFromOrganizationPartner(
  57. undefined,
  58. project.integration_source
  59. )
  60. const projectStatus = inferProjectStatus(project.status)
  61. return (
  62. <>
  63. <li className="list-none h-min">
  64. <CardButton
  65. linkHref={rewriteHref ? rewriteHref : `/project/${projectRef}`}
  66. className="h-44 px-0! group pt-5 pb-0 overflow-hidden relative"
  67. hideChevron
  68. title={
  69. <div className="w-full flex flex-col gap-y-4 justify-between px-5">
  70. <div className="flex flex-col gap-y-0.5 relative">
  71. <div className="flex items-center justify-between">
  72. <h5 className="text-sm shrink truncate pr-5">{name}</h5>
  73. <div onClick={(e) => e.preventDefault()}>
  74. <DropdownMenu>
  75. <DropdownMenuTrigger asChild>
  76. <Button
  77. type="text"
  78. icon={<MoreVertical size={14} />}
  79. className="w-6 h-6 px-0"
  80. onClick={(e) => {
  81. e.stopPropagation()
  82. e.preventDefault()
  83. }}
  84. onPointerDown={(e) => e.stopPropagation()}
  85. />
  86. </DropdownMenuTrigger>
  87. <DropdownMenuContent align="end" className="w-48">
  88. <DropdownMenuItem
  89. className="gap-x-2"
  90. onClick={(e) => {
  91. e.stopPropagation()
  92. copyToClipboard(projectRef)
  93. toast.success('Copied project ID to clipboard')
  94. }}
  95. >
  96. <Copy size={14} />
  97. <span>Copy project ID</span>
  98. </DropdownMenuItem>
  99. <DropdownMenuItem
  100. className="gap-x-2"
  101. onClick={(e) => {
  102. e.stopPropagation()
  103. router.push(`/project/${projectRef}/settings/general`)
  104. }}
  105. >
  106. <Settings size={14} />
  107. <span>Settings</span>
  108. </DropdownMenuItem>
  109. </DropdownMenuContent>
  110. </DropdownMenu>
  111. </div>
  112. </div>
  113. <p className="text-sm text-foreground-lighter">{desc}</p>
  114. </div>
  115. <div className="flex items-center gap-x-1.5 relative overflow-hidden">
  116. {project.status !== 'INACTIVE' && projectHomepageShowInstanceSize && (
  117. <ComputeBadgeWrapper
  118. slug={slug}
  119. projectRef={project.ref}
  120. cloudProvider={project.cloud_provider}
  121. computeSize={getComputeSize(project)}
  122. resourceWarnings={resourceWarnings}
  123. badgeClassName="text-[10px] leading-none tracking-[0.07em]"
  124. />
  125. )}
  126. {isVercelIntegrated && (
  127. <div className="bg-surface-100 w-5 h-5 p-1 border border-strong rounded-md flex items-center justify-center text-black dark:text-white">
  128. <InlineSVG
  129. src={`${BASE_PATH}/img/icons/vercel-icon.svg`}
  130. title="Vercel Icon"
  131. className="w-3"
  132. />
  133. </div>
  134. )}
  135. <PartnerIcon organization={{ managed_by: projectManagedBy }} />
  136. {isGithubIntegrated && (
  137. <div className="bg-surface-100 flex items-center gap-x-0.5 h-5 pr-1 border border-strong rounded-md min-w-0">
  138. <div className="w-5 h-5 p-1 flex items-center justify-center shrink-0">
  139. <Github size={12} strokeWidth={1.5} />
  140. </div>
  141. <p className="text-xs text-foreground-light truncate">{githubRepository}</p>
  142. </div>
  143. )}
  144. </div>
  145. </div>
  146. }
  147. footer={
  148. <ProjectCardStatus projectStatus={projectStatus} resourceWarnings={resourceWarnings} />
  149. }
  150. containerElement={<ProjectIndexPageLink projectRef={projectRef} />}
  151. />
  152. </li>
  153. </>
  154. )
  155. }