|
1 | 1 | import type { Metadata } from "next"; |
| 2 | +import Image from "next/image"; |
| 3 | +import Link from "next/link"; |
2 | 4 |
|
3 | 5 | export const metadata: Metadata = { |
4 | 6 | title: "Sponsors — Team whIRLwind", |
5 | 7 | description: "Our generous sponsors.", |
6 | 8 | }; |
7 | 9 |
|
8 | | -const sponsors = new Array(8).fill(0).map((_, i) => ({ |
9 | | - id: i + 1, |
10 | | - name: `Sponsor ${String.fromCharCode(65 + i)}`, |
11 | | -})); |
| 10 | +type Sponsor = { |
| 11 | + name: string; |
| 12 | + website?: string; |
| 13 | + contribution?: string; |
| 14 | + notes?: string; |
| 15 | + logo?: string; |
| 16 | + logoAlt?: string; |
| 17 | + logoWidth?: number; |
| 18 | + logoHeight?: number; |
| 19 | +}; |
| 20 | + |
| 21 | +// TODO: replace placeholder data with the team's actual sponsors when ready. |
| 22 | +const sponsors: Sponsor[] = [ |
| 23 | + { |
| 24 | + name: "Rerun", |
| 25 | + website: "https://rerun.io", |
| 26 | + logo: "/sponsors/rerun.svg", |
| 27 | + logoAlt: "Rerun logo", |
| 28 | + logoWidth: 94, |
| 29 | + logoHeight: 28, |
| 30 | + }, |
| 31 | + { |
| 32 | + name: "StartUp Village", |
| 33 | + website: "https://startupvillage.nl", |
| 34 | + logo: "/sponsors/startup_village.webp", |
| 35 | + logoAlt: "StartUp Village logo", |
| 36 | + logoWidth: 1500, |
| 37 | + logoHeight: 756, |
| 38 | + }, |
| 39 | +]; |
| 40 | + |
| 41 | +function formatWebsite(url?: string): string | null { |
| 42 | + if (!url) return null; |
| 43 | + return url.replace(/^https?:\/\//, "").replace(/\/$/, ""); |
| 44 | +} |
12 | 45 |
|
13 | 46 | export default function SponsorsPage() { |
14 | 47 | return ( |
15 | 48 | <section className="page"> |
16 | 49 | <div className="container"> |
17 | 50 | <h1>Sponsors</h1> |
18 | 51 | <p className="lead"> |
19 | | - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam |
20 | | - bibendum, eros non ultricies aliquet, lorem arcu tempor lorem, vitae |
21 | | - dictum arcu mi non eros. |
| 52 | + These partners keep our robots rolling and make it possible to share |
| 53 | + the work beyond the lab. |
22 | 54 | </p> |
23 | 55 |
|
24 | | - <div className="grid"> |
25 | | - {sponsors.map((s) => ( |
26 | | - <div key={s.id} className="panel px-4 py-8 text-center"> |
| 56 | + <div className="space-y-12"> |
| 57 | + {sponsors.map((sponsor) => { |
| 58 | + const isRerun = sponsor.name === "Rerun"; |
| 59 | + const imageStyle = { |
| 60 | + width: "auto" as const, |
| 61 | + maxWidth: isRerun ? "420px" : "240px", |
| 62 | + height: isRerun ? "96px" : "80px", |
| 63 | + }; |
| 64 | + const hasDetails = Boolean( |
| 65 | + sponsor.contribution || sponsor.notes, |
| 66 | + ); |
| 67 | + const captionClass = [ |
| 68 | + "flex flex-col items-center text-center", |
| 69 | + isRerun ? "gap-2" : "gap-3", |
| 70 | + ].join(" "); |
| 71 | + const logoWrapperClass = [ |
| 72 | + "flex shrink-0 items-center justify-center rounded-xl", |
| 73 | + isRerun |
| 74 | + ? "px-4 pt-2 pb-1" |
| 75 | + : "px-4 py-4 bg-white shadow-sm border border-[rgba(255,255,255,0.12)]", |
| 76 | + ].join(" "); |
| 77 | + |
| 78 | + return ( |
27 | 79 | <div |
28 | | - className="grid h-[90px] w-full place-items-center rounded-xl border border-dashed border-[rgba(251,146,60,0.35)] bg-[linear-gradient(180deg,rgba(27,39,66,0.3),rgba(27,39,66,0.1))]" |
29 | | - aria-label={`${s.name} placeholder`} |
| 80 | + key={sponsor.name} |
| 81 | + className="sponsor-entry space-y-6" |
30 | 82 | > |
31 | | - <span className="font-bold text-(--ink-dim)">{s.name}</span> |
| 83 | + <div |
| 84 | + className={[ |
| 85 | + "flex flex-col gap-6 sm:gap-10", |
| 86 | + hasDetails ? "sm:flex-row sm:items-start" : "items-start", |
| 87 | + ].join(" ")} |
| 88 | + > |
| 89 | + <div className={captionClass}> |
| 90 | + {sponsor.logo && |
| 91 | + sponsor.logoWidth && |
| 92 | + sponsor.logoHeight && ( |
| 93 | + <div className={logoWrapperClass}> |
| 94 | + <Image |
| 95 | + src={sponsor.logo} |
| 96 | + alt={sponsor.logoAlt ?? `${sponsor.name} logo`} |
| 97 | + width={sponsor.logoWidth ?? 200} |
| 98 | + height={sponsor.logoHeight ?? 80} |
| 99 | + sizes="(min-width: 640px) 280px, 60vw" |
| 100 | + className="object-contain" |
| 101 | + style={imageStyle} |
| 102 | + /> |
| 103 | + </div> |
| 104 | + )} |
| 105 | + |
| 106 | + {formatWebsite(sponsor.website) && sponsor.website && ( |
| 107 | + <Link |
| 108 | + href={sponsor.website} |
| 109 | + target={ |
| 110 | + sponsor.website.startsWith("http") |
| 111 | + ? "_blank" |
| 112 | + : undefined |
| 113 | + } |
| 114 | + rel={ |
| 115 | + sponsor.website.startsWith("http") |
| 116 | + ? "noopener noreferrer" |
| 117 | + : undefined |
| 118 | + } |
| 119 | + className="text-[0.85rem] uppercase tracking-wide text-(--ink-muted) transition-colors duration-200 hover:text-(--orange-400)" |
| 120 | + > |
| 121 | + {formatWebsite(sponsor.website)} |
| 122 | + </Link> |
| 123 | + )} |
| 124 | + </div> |
| 125 | + |
| 126 | + {hasDetails && ( |
| 127 | + <div className="space-y-3 sm:flex-1"> |
| 128 | + {sponsor.contribution && ( |
| 129 | + <p className="max-w-3xl text-[0.95rem] text-(--ink-dim)"> |
| 130 | + {sponsor.contribution} |
| 131 | + </p> |
| 132 | + )} |
| 133 | + |
| 134 | + {sponsor.notes && ( |
| 135 | + <p className="text-[0.9rem] text-(--ink-muted)"> |
| 136 | + {sponsor.notes} |
| 137 | + </p> |
| 138 | + )} |
| 139 | + </div> |
| 140 | + )} |
| 141 | + </div> |
32 | 142 | </div> |
33 | | - </div> |
34 | | - ))} |
| 143 | + ); |
| 144 | + })} |
35 | 145 | </div> |
36 | 146 | </div> |
37 | 147 | </section> |
|
0 commit comments