|
1 | 1 | import Image from 'next/image'; |
2 | | -import { IconExternalLink } from '@tabler/icons-react'; |
| 2 | +import { IconExternalLink, IconWorld } from '@tabler/icons-react'; |
3 | 3 | import { EcosystemFieldData } from '../../data/ecosystemData'; |
4 | 4 |
|
5 | 5 | interface AppCardV2Props { |
6 | | - app: { fieldData: EcosystemFieldData }; |
| 6 | + app?: { fieldData: EcosystemFieldData }; |
| 7 | + title?: string; |
| 8 | + description?: string; |
| 9 | + href?: string; |
| 10 | + icon?: React.ReactNode; |
| 11 | + logoUrl?: string; |
7 | 12 | } |
8 | 13 |
|
9 | | -export default function AppCardV2({ app }: AppCardV2Props) { |
10 | | - if (!app) return null; |
| 14 | +export default function AppCardV2({ app, title, description, href, icon, logoUrl: logoUrlOverride }: AppCardV2Props) { |
| 15 | + if (!app && !title) return null; |
11 | 16 |
|
12 | | - const { name, logo, link, 'short-description': desc, 'integration-guide-link': integration, priority } = app.fieldData; |
| 17 | + let name = title; |
| 18 | + let desc = description; |
| 19 | + let finalIntegrationLink = href; |
| 20 | + let logoUrl: string | undefined = logoUrlOverride; |
| 21 | + let linkText = 'Visit Website'; |
13 | 22 |
|
14 | | - const integrationGuideOverrides: Record<string, string> = { |
15 | | - 'The Graph': '/evm/indexer-providers/the-graph', |
16 | | - 'Covalent': '/evm/indexer-providers/goldrush', |
17 | | - 'Goldsky': '/evm/indexer-providers/goldsky', |
18 | | - 'Alchemy': '/evm/indexer-providers/alchemy', |
19 | | - 'Envio': 'https://docs.envio.dev/docs/HyperIndex/getting-started', |
20 | | - 'Sonarverse': 'https://docs.sonarplatform.io/', |
21 | | - 'Ormi': 'https://docs.orai.io/developer-guides/overview/subquery' |
22 | | - }; |
| 23 | + if (app) { |
| 24 | + const { name: appName, logo, link, 'short-description': appDesc, 'integration-guide-link': integration } = app.fieldData; |
| 25 | + name = appName; |
| 26 | + desc = appDesc; |
| 27 | + logoUrl = logoUrlOverride || logo?.url; |
23 | 28 |
|
24 | | - const finalIntegrationLink = integrationGuideOverrides[name] || integration; |
25 | | - const isExternalLink = finalIntegrationLink && !finalIntegrationLink.startsWith('/'); |
26 | | - const linkText = isExternalLink ? 'Documentation' : 'Integration Guide'; |
| 29 | + const integrationGuideOverrides: Record<string, string> = { |
| 30 | + 'The Graph': '/evm/indexer-providers/the-graph', |
| 31 | + Covalent: '/evm/indexer-providers/goldrush', |
| 32 | + Goldsky: '/evm/indexer-providers/goldsky', |
| 33 | + Alchemy: '/evm/indexer-providers/alchemy' |
| 34 | + }; |
| 35 | + |
| 36 | + finalIntegrationLink = integrationGuideOverrides[appName] || integration; |
| 37 | + const isExternalLinkData = finalIntegrationLink && !finalIntegrationLink.startsWith('/'); |
| 38 | + linkText = isExternalLinkData ? 'Documentation' : 'Integration Guide'; |
| 39 | + |
| 40 | + if (!logoUrl) return null; |
| 41 | + } |
27 | 42 |
|
28 | | - if (!logo) return null; |
| 43 | + const isExternalLink = finalIntegrationLink && !finalIntegrationLink.startsWith('/'); |
29 | 44 |
|
30 | 45 | return ( |
31 | 46 | <div className='group relative overflow-hidden transition-all duration-300 ease-out transform-gpu will-change-transform hover:scale-[1.02] hover:shadow-xl hover:shadow-black/10 dark:hover:shadow-black/30 rounded-xl border backdrop-blur-sm bg-neutral-50/80 dark:bg-neutral-900/80 border-neutral-200/50 dark:border-neutral-800/50 hover:bg-white dark:hover:bg-neutral-900 hover:border-neutral-300 dark:hover:border-neutral-700 p-5 h-full flex flex-col'> |
32 | 47 | <div className='absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-500 bg-gradient-to-br from-white/10 via-transparent to-transparent dark:from-white/5 dark:via-transparent dark:to-transparent pointer-events-none rounded-xl' /> |
33 | | - |
| 48 | + |
34 | 49 | <div className='absolute inset-0 rounded-xl opacity-0 group-hover:opacity-100 transition-all duration-700 bg-gradient-to-r from-red-900/15 via-red-800/10 to-red-900/15 dark:from-red-800/20 dark:via-red-700/15 dark:to-red-800/20 blur-sm -z-10 transform scale-105' /> |
35 | | - |
| 50 | + |
36 | 51 | <div className='flex flex-col gap-4 relative z-10'> |
37 | 52 | <div className='flex items-start justify-between'> |
38 | 53 | <figure className='w-12 h-12 rounded-lg overflow-hidden flex-shrink-0 bg-neutral-200/50 dark:bg-neutral-800/50 group-hover:bg-red-900/10 dark:group-hover:bg-red-800/20 transition-all duration-300 group-hover:scale-110 group-hover:rotate-3 flex items-center justify-center'> |
39 | | - <Image |
40 | | - src={logo.url} |
41 | | - alt={name} |
42 | | - width={48} |
43 | | - height={48} |
44 | | - className='object-cover w-full h-full transform transition-all duration-300 group-hover:scale-110' |
45 | | - /> |
| 54 | + {logoUrl ? ( |
| 55 | + <Image |
| 56 | + src={logoUrl} |
| 57 | + alt={name || ''} |
| 58 | + width={48} |
| 59 | + height={48} |
| 60 | + className='object-cover w-full h-full transform transition-all duration-300 group-hover:scale-110' |
| 61 | + /> |
| 62 | + ) : ( |
| 63 | + icon || <IconWorld className='text-neutral-500 group-hover:text-red-600 transition-colors duration-300' size={24} /> |
| 64 | + )} |
46 | 65 | </figure> |
47 | 66 | </div> |
48 | | - |
| 67 | + |
49 | 68 | <div className='flex flex-col gap-2 flex-grow'> |
50 | | - <h3 className='text-base font-bold text-neutral-800 dark:text-neutral-200 group-hover:text-red-800 dark:group-hover:text-red-300 transition-colors duration-300 leading-tight'>{name}</h3> |
51 | | - {desc && ( |
52 | | - <p className='text-sm text-neutral-600 dark:text-neutral-400 leading-relaxed line-clamp-2 flex-grow'> |
53 | | - {desc} |
54 | | - </p> |
55 | | - )} |
| 69 | + <h3 className='text-base font-bold text-neutral-800 dark:text-neutral-200 group-hover:text-red-800 dark:group-hover:text-red-300 transition-colors duration-300 leading-tight'> |
| 70 | + {name} |
| 71 | + </h3> |
| 72 | + {desc && <p className='text-sm text-neutral-600 dark:text-neutral-400 leading-relaxed line-clamp-2 flex-grow'>{desc}</p>} |
56 | 73 | </div> |
57 | | - |
| 74 | + |
58 | 75 | {finalIntegrationLink && ( |
59 | 76 | <div className='flex items-center mt-auto pt-2'> |
60 | 77 | <a |
61 | 78 | href={finalIntegrationLink} |
62 | 79 | target={isExternalLink ? '_blank' : '_self'} |
63 | 80 | rel={isExternalLink ? 'noopener noreferrer' : undefined} |
64 | | - className='inline-flex items-center gap-1.5 text-sm font-medium text-red-800 dark:text-red-300 hover:text-red-900 dark:hover:text-red-200 transition-colors duration-200 group/link' |
65 | | - > |
| 81 | + className='inline-flex items-center gap-1.5 text-sm font-medium text-red-800 dark:text-red-300 hover:text-red-900 dark:hover:text-red-200 transition-colors duration-200 group/link'> |
66 | 82 | <span>{linkText}</span> |
67 | | - <IconExternalLink |
68 | | - size={14} |
69 | | - className="transition-all duration-300 group-hover/link:ml-1 group-hover/link:scale-110" |
70 | | - /> |
| 83 | + <IconExternalLink size={14} className='transition-all duration-300 group-hover/link:ml-1 group-hover/link:scale-110' /> |
71 | 84 | </a> |
72 | 85 | </div> |
73 | 86 | )} |
74 | | - |
75 | | - <div className="w-full h-0.5 bg-gradient-to-r from-transparent via-red-800/20 dark:via-red-300/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300" /> |
| 87 | + |
| 88 | + <div className='w-full h-0.5 bg-gradient-to-r from-transparent via-red-800/20 dark:via-red-300/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300' /> |
76 | 89 | </div> |
77 | 90 | </div> |
78 | 91 | ); |
|
0 commit comments