IntegrationCard.tsx114 lines · main
| 1 | import { BadgeCheck } from 'lucide-react' |
| 2 | import Image from 'next/image' |
| 3 | import Link from 'next/link' |
| 4 | import { Badge, Card, CardContent, cn } from 'ui' |
| 5 | import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader' |
| 6 | |
| 7 | import { IntegrationDefinition } from './Integrations.constants' |
| 8 | import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' |
| 9 | |
| 10 | type IntegrationCardProps = IntegrationDefinition & { |
| 11 | isInstalled?: boolean |
| 12 | featured?: boolean |
| 13 | image?: string |
| 14 | } |
| 15 | |
| 16 | const INTEGRATION_CARD_STYLE = cn( |
| 17 | 'w-full h-full bg-surface-100 hover:bg-surface-200 hover:border-strong', |
| 18 | 'border border-border rounded-md ease-out duration-200 transition-all' |
| 19 | ) |
| 20 | |
| 21 | export const IntegrationLoadingCard = () => { |
| 22 | return ( |
| 23 | <div className={cn(INTEGRATION_CARD_STYLE, 'pl-5 pr-6 py-3 gap-3 inline-flex h-[110px]')}> |
| 24 | <div className="w-10 h-10 relative"> |
| 25 | <ShimmeringLoader className="w-full h-full bg-foreground-light border rounded-md" /> |
| 26 | </div> |
| 27 | <div className="grow basis-0 w-full flex flex-col justify-between items-start gap-y-2"> |
| 28 | <div className="w-full flex-col justify-start items-start gap-y-1 flex"> |
| 29 | <ShimmeringLoader className="w-3/4 py-2.5" /> |
| 30 | <ShimmeringLoader className="w-full py-2.5" /> |
| 31 | </div> |
| 32 | </div> |
| 33 | </div> |
| 34 | ) |
| 35 | } |
| 36 | |
| 37 | export const IntegrationCard = ({ |
| 38 | id, |
| 39 | listingId, |
| 40 | status, |
| 41 | name, |
| 42 | icon, |
| 43 | description, |
| 44 | isInstalled, |
| 45 | featured = false, |
| 46 | image, |
| 47 | }: IntegrationCardProps) => { |
| 48 | const { data: project } = useSelectedProjectQuery() |
| 49 | const shouldShowOfficialBadge = !listingId |
| 50 | |
| 51 | if (featured) { |
| 52 | return ( |
| 53 | <Link href={`/project/${project?.ref}/integrations/${id}/overview`} className="h-full"> |
| 54 | <Card className="h-full"> |
| 55 | {/* Full-width image/icon at the top */} |
| 56 | <div className="w-full h-24 bg-surface-400 rounded-t-md flex items-center justify-center overflow-hidden relative"> |
| 57 | {image ? ( |
| 58 | <Image |
| 59 | fill |
| 60 | src={image} |
| 61 | alt={`${name} integration`} |
| 62 | className="w-full h-full object-cover invert dark:invert-0" |
| 63 | objectFit="cover" |
| 64 | /> |
| 65 | ) : ( |
| 66 | <div className="w-12 h-12 text-foreground relative"> |
| 67 | {icon({ className: 'w-full h-full text-foreground' })} |
| 68 | </div> |
| 69 | )} |
| 70 | </div> |
| 71 | <CardContent className="p-6 px-4"> |
| 72 | <div className="flex-col justify-start items-center text-center gap-y-0.5 flex"> |
| 73 | <h3>{name}</h3> |
| 74 | <p className="text-foreground-light text-sm line-clamp-3">{description}</p> |
| 75 | <div className="flex items-center gap-x-1 mt-4"> |
| 76 | {status && <Badge variant="warning">{status}</Badge>} |
| 77 | {shouldShowOfficialBadge && <Badge>Official</Badge>} |
| 78 | </div> |
| 79 | </div> |
| 80 | </CardContent> |
| 81 | </Card> |
| 82 | </Link> |
| 83 | ) |
| 84 | } |
| 85 | |
| 86 | return ( |
| 87 | <Link href={`/project/${project?.ref}/integrations/${id}/overview`} className="h-full"> |
| 88 | <Card className="h-full"> |
| 89 | <CardContent className="flex flex-col p-4 @2xl:p-6 h-full"> |
| 90 | <div className="flex items-start justify-between mb-4"> |
| 91 | <div className="shrink-0 w-10 h-10 relative bg-white border rounded-md flex items-center justify-center"> |
| 92 | {icon()} |
| 93 | </div> |
| 94 | {isInstalled && ( |
| 95 | <div className="flex items-center gap-x-1"> |
| 96 | <BadgeCheck size={14} className="text-brand-link" /> |
| 97 | <span className="text-brand-link text-xs">Installed</span> |
| 98 | </div> |
| 99 | )} |
| 100 | </div> |
| 101 | <div className="flex-col justify-start items-start gap-y-0.5 flex flex-1"> |
| 102 | <h3 className="text-foreground text-sm">{name}</h3> |
| 103 | |
| 104 | <p className="text-foreground-light text-xs flex-1">{description}</p> |
| 105 | <div className="flex items-center gap-x-1 mt-4"> |
| 106 | {status && <Badge variant="warning">{status}</Badge>} |
| 107 | {shouldShowOfficialBadge && <Badge>Official</Badge>} |
| 108 | </div> |
| 109 | </div> |
| 110 | </CardContent> |
| 111 | </Card> |
| 112 | </Link> |
| 113 | ) |
| 114 | } |