Skip to content

Feat/bayarea#11

Open
SimonBurmer wants to merge 34 commits intomainfrom
feat/bayarea
Open

Feat/bayarea#11
SimonBurmer wants to merge 34 commits intomainfrom
feat/bayarea

Conversation

@SimonBurmer
Copy link
Copy Markdown
Collaborator

@SimonBurmer SimonBurmer commented Mar 23, 2026

Summary by CodeRabbit

  • New Features

    • Launched a Bay Area program page accessible from main navigation
    • Interactive year selector with per-year highlights, timeline, schedules, hosts and team sections (including preview year)
    • Program page includes hero highlights, program overview grid, CTA (contact mail + external START link)
    • New member card component and dynamic member lookup for team listings
  • Chores

    • Added baseline ESLint configuration
  • Other

    • Replaced external Bay Area link with the new internal program route

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 23, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a "Start Goes Bay Area" feature: a nav link and dedicated Next.js page with hero/overview/year-tabs/CTA, a typed static data module, a client-side year-tabs component that fetches members, a MemberCard component, a Member Journey link update, and an ESLint config.

Changes

Cohort / File(s) Summary
Navigation
app/page.tsx
Inserted new nav item linking to /start-goes-bay-area, placed between /for-partners and /startups, using existing link styles.
Bay Area Page
app/start-goes-bay-area/page.tsx
New Next.js page exporting metadata and default component rendering hero cards, PROGRAM OVERVIEW grid, YEAR OVERVIEW (uses BayAreaYearTabs), and a CTA with mailto and external START link.
Static Data & Types
lib/startGoesBayAreaData.ts
New typed data module defining interfaces/types and exporting bayAreaHeroHighlights, bayAreaOverviewItems, and bayAreaYearContent (2025–2027) with inlined content.
Year Tabs UI
components/BayAreaYearTabs.tsx
New client component with activeYear state (default '2026'), imports bayAreaYearContent, fetches /api/members, renders year selector (Preview badges), highlights, grouped trip timeline, hosts, and orga team (matches fetched members to team entries).
Member Card
components/MemberCard.tsx
New presentational component rendering member image, gradient overlay, name, role, and optional LinkedIn anchor/button.
Member Journey Link
app/member-journey/page.tsx
Replaced external START goes Bay Area absolute URL with internal relative route /start-goes-bay-area while retaining target/rel attributes.
Lint config
.eslintrc.json
Added ESLint configuration extending next/core-web-vitals and next/typescript.

Sequence Diagram(s)

sequenceDiagram
  participant User as User
  participant Browser as Browser/UI
  participant Page as /start-goes-bay-area Page
  participant Tabs as BayAreaYearTabs (client)
  participant Data as startGoesBayAreaData
  participant API as /api/members

  User->>Browser: Navigate to /start-goes-bay-area
  Browser->>Page: Request SSR page
  Page-->>Browser: Serve page shell + metadata
  Browser->>Tabs: Mount client component
  Tabs->>Data: Import bayAreaYearContent
  Tabs->>API: Fetch /api/members
  API-->>Tabs: Return members list (or error)
  Tabs-->>Browser: Render default year ('2026') content (hero, highlights, timeline, hosts, team)
  User->>Tabs: Click year tab (e.g., '2025' or '2027')
  Tabs->>Data: Select corresponding year entry
  Tabs-->>Browser: Re-render sections with selected year's data (or preview message)
  User->>Browser: Click CTA (mailto or external START link)
  Browser->>External: Open mail client or external site (target="_blank")
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐇 I hopped across the code today,
A Bay Area path now lights our way,
Tabs and timelines, visits to share,
Members fetched with thoughtful care,
🥕 Onward we hop — adventures prepare!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Feat/bayarea' is vague and does not clearly convey the main purpose of the pull request; it uses generic naming convention without meaningful context about what was built or changed. Use a descriptive title like 'Add START goes Bay Area program page' to clearly communicate the primary change and make it scannable in commit history.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/bayarea

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
start-munich Ready Ready Preview, Comment Apr 22, 2026 9:52am
website-special-pages-y5ch Ready Ready Preview, Comment Apr 22, 2026 9:52am

- Created a new BayAreaYearTabs component to display year-specific content for the Bay Area.
- Added ESLint configuration for Next.js with TypeScript support.
- Introduced SVG logos for various partners and organizations, including Boost VC, CodeRabbit, Coherence Neuro, and others.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
app/member-journey/page.tsx (1)

729-731: Use same-tab navigation for this internal route.

Line 729 now points to an internal page, so forcing a new tab is usually unnecessary and breaks normal in-app navigation flow.

♻️ Proposed tweak
               <a
                 href="/start-goes-bay-area"
-                target="_blank"
-                rel="noopener noreferrer"
                 className="group relative overflow-hidden bg-white/5 border border-white/10 hover:border-brand-pink/50 transition-all duration-300 cursor-pointer"
               >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/member-journey/page.tsx` around lines 729 - 731, The anchor linking to
"/start-goes-bay-area" is an internal route but currently forces a new tab via
target="_blank" (and rel="noopener noreferrer"); update the element that uses
href="/start-goes-bay-area" to perform same-tab navigation by removing target
and rel attributes (or replace the anchor with Next.js Link if used elsewhere)
so the internal route opens in the same tab and preserves in-app navigation
behavior.
components/BayAreaYearTabs.tsx (2)

9-10: Guard against an empty bayAreaYearContent array before dereferencing.

If this data ever becomes empty, activeContent becomes undefined and the render path will crash.

🛡️ Defensive guard
 export default function BayAreaYearTabs() {
     const [activeYear, setActiveYear] = useState<BayAreaYearId>('2026')

-    const activeContent = bayAreaYearContent.find((item) => item.id === activeYear) ?? bayAreaYearContent[0]
+    if (bayAreaYearContent.length === 0) {
+        return null
+    }
+    const activeContent = bayAreaYearContent.find((item) => item.id === activeYear) ?? bayAreaYearContent[0]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/BayAreaYearTabs.tsx` around lines 9 - 10, The current assignment
to activeContent uses bayAreaYearContent.find(...) and falls back to
bayAreaYearContent[0], which will throw if bayAreaYearContent is empty; guard by
checking bayAreaYearContent.length === 0 first and return an appropriate
fallback (e.g., null render or a default object) or early-return from the
component, and ensure any downstream render logic that uses activeContent
(referenced by activeContent and activeYear) handles the empty-case safely.

13-173: Use Card primitives for content shells instead of hand-rolling with Tailwind classes.

The project has a Card primitive available (components/ui/card.tsx), but this component manually styles card-like structures with bg-white/5 border border-white/10 p-4 patterns. Migrate the bordered containers (Year Highlights section, Trip Timeline, Detailed Bay Area Visits, Hosts & Partners, Orga Team) to the shadcn/ui Card component for consistency. Note: A tabs primitive is not available in the project, so the year switcher can remain as custom button logic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/BayAreaYearTabs.tsx` around lines 13 - 173, The file
BayAreaYearTabs currently hand-rolls multiple card-like containers (look for
divs with classes like "bg-white/5 border border-white/10 p-4" and "bg-white/5
border border-white/10 p-6 md:p-8") — replace those with the Card primitive from
components/ui/card.tsx: import Card (and CardHeader/CardContent if available) at
the top of BayAreaYearTabs, wrap each affected section (Year Highlights
container with md:col-span-3, the Trip Timeline block, the Detailed Bay Area
Visits block, the Hosts & Partners block, and the Orga Team block) in a Card
component instead of the manual bg/border/p class divs, move the section
headings into CardHeader or keep them in CardContent as appropriate, and remove
the duplicated bg/border/p-* Tailwind classes so styling is provided by the Card
primitive while preserving existing inner markup (lists, maps, keys, and
classNames for internal elements).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/BayAreaYearTabs.tsx`:
- Around line 18-29: Add a programmatic selected state by adding aria-pressed to
each year button so assistive tech can detect the active toggle; in the button
rendered inside the map (the element using key={year.id}, onClick={() =>
setActiveYear(year.id)} and the isActive variable), set aria-pressed to the
boolean isActive value (not a string) so the active state reflects the current
activeYear managed by setActiveYear.

---

Nitpick comments:
In `@app/member-journey/page.tsx`:
- Around line 729-731: The anchor linking to "/start-goes-bay-area" is an
internal route but currently forces a new tab via target="_blank" (and
rel="noopener noreferrer"); update the element that uses
href="/start-goes-bay-area" to perform same-tab navigation by removing target
and rel attributes (or replace the anchor with Next.js Link if used elsewhere)
so the internal route opens in the same tab and preserves in-app navigation
behavior.

In `@components/BayAreaYearTabs.tsx`:
- Around line 9-10: The current assignment to activeContent uses
bayAreaYearContent.find(...) and falls back to bayAreaYearContent[0], which will
throw if bayAreaYearContent is empty; guard by checking
bayAreaYearContent.length === 0 first and return an appropriate fallback (e.g.,
null render or a default object) or early-return from the component, and ensure
any downstream render logic that uses activeContent (referenced by activeContent
and activeYear) handles the empty-case safely.
- Around line 13-173: The file BayAreaYearTabs currently hand-rolls multiple
card-like containers (look for divs with classes like "bg-white/5 border
border-white/10 p-4" and "bg-white/5 border border-white/10 p-6 md:p-8") —
replace those with the Card primitive from components/ui/card.tsx: import Card
(and CardHeader/CardContent if available) at the top of BayAreaYearTabs, wrap
each affected section (Year Highlights container with md:col-span-3, the Trip
Timeline block, the Detailed Bay Area Visits block, the Hosts & Partners block,
and the Orga Team block) in a Card component instead of the manual bg/border/p
class divs, move the section headings into CardHeader or keep them in
CardContent as appropriate, and remove the duplicated bg/border/p-* Tailwind
classes so styling is provided by the Card primitive while preserving existing
inner markup (lists, maps, keys, and classNames for internal elements).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 78ba234c-5161-41c8-8dd4-3f995446b5aa

📥 Commits

Reviewing files that changed from the base of the PR and between 5776a60 and e3e6cee.

⛔ Files ignored due to path filters (22)
  • public/bayarea/logos/boost-vc.svg is excluded by !**/*.svg
  • public/bayarea/logos/coderabbit.svg is excluded by !**/*.svg
  • public/bayarea/logos/coherence-neuro.svg is excluded by !**/*.svg
  • public/bayarea/logos/etched.svg is excluded by !**/*.svg
  • public/bayarea/logos/founders-inc.svg is excluded by !**/*.svg
  • public/bayarea/logos/gacc-west.svg is excluded by !**/*.svg
  • public/bayarea/logos/google-x.svg is excluded by !**/*.svg
  • public/bayarea/logos/inflammatix.svg is excluded by !**/*.svg
  • public/bayarea/logos/intrinsic.svg is excluded by !**/*.svg
  • public/bayarea/logos/magrathea.svg is excluded by !**/*.svg
  • public/bayarea/logos/maschmeyer-group.svg is excluded by !**/*.svg
  • public/bayarea/logos/nvidia.svg is excluded by !**/*.svg
  • public/bayarea/logos/pillsbury.svg is excluded by !**/*.svg
  • public/bayarea/logos/rippling.svg is excluded by !**/*.svg
  • public/bayarea/logos/satlyt-ai.svg is excluded by !**/*.svg
  • public/bayarea/logos/savor.svg is excluded by !**/*.svg
  • public/bayarea/logos/sofar-ocean.svg is excluded by !**/*.svg
  • public/bayarea/logos/stanford-university.svg is excluded by !**/*.svg
  • public/bayarea/logos/start2-group.svg is excluded by !**/*.svg
  • public/bayarea/logos/the-residency.svg is excluded by !**/*.svg
  • public/bayarea/logos/windborne-systems.svg is excluded by !**/*.svg
  • public/bayarea/logos/y-combinator.svg is excluded by !**/*.svg
📒 Files selected for processing (5)
  • .eslintrc.json
  • app/member-journey/page.tsx
  • app/start-goes-bay-area/page.tsx
  • components/BayAreaYearTabs.tsx
  • lib/startGoesBayAreaData.ts
✅ Files skipped from review due to trivial changes (1)
  • .eslintrc.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/start-goes-bay-area/page.tsx

Comment thread components/BayAreaYearTabs.tsx Outdated
Comment on lines +18 to +29
<button
key={year.id}
type="button"
onClick={() => setActiveYear(year.id)}
className={`px-4 py-2 border text-sm font-bold uppercase tracking-wide transition-colors ${isActive
? 'bg-brand-pink text-white border-brand-pink'
: 'bg-white/5 text-gray-300 border-white/15 hover:bg-white/10'
}`}
>
{year.label}
{year.isPreview ? <span className="ml-2 text-xs opacity-80">Preview</span> : null}
</button>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Expose active year state for assistive tech.

The year toggle is visually clear, but there is no programmatic “selected” state. Add aria-pressed on each button.

♿ Proposed accessibility fix
                         <button
                             key={year.id}
                             type="button"
                             onClick={() => setActiveYear(year.id)}
+                            aria-pressed={isActive}
                             className={`px-4 py-2 border text-sm font-bold uppercase tracking-wide transition-colors ${isActive
                                 ? 'bg-brand-pink text-white border-brand-pink'
                                 : 'bg-white/5 text-gray-300 border-white/15 hover:bg-white/10'
                                 }`}
                         >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<button
key={year.id}
type="button"
onClick={() => setActiveYear(year.id)}
className={`px-4 py-2 border text-sm font-bold uppercase tracking-wide transition-colors ${isActive
? 'bg-brand-pink text-white border-brand-pink'
: 'bg-white/5 text-gray-300 border-white/15 hover:bg-white/10'
}`}
>
{year.label}
{year.isPreview ? <span className="ml-2 text-xs opacity-80">Preview</span> : null}
</button>
<button
key={year.id}
type="button"
onClick={() => setActiveYear(year.id)}
aria-pressed={isActive}
className={`px-4 py-2 border text-sm font-bold uppercase tracking-wide transition-colors ${isActive
? 'bg-brand-pink text-white border-brand-pink'
: 'bg-white/5 text-gray-300 border-white/15 hover:bg-white/10'
}`}
>
{year.label}
{year.isPreview ? <span className="ml-2 text-xs opacity-80">Preview</span> : null}
</button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/BayAreaYearTabs.tsx` around lines 18 - 29, Add a programmatic
selected state by adding aria-pressed to each year button so assistive tech can
detect the active toggle; in the button rendered inside the map (the element
using key={year.id}, onClick={() => setActiveYear(year.id)} and the isActive
variable), set aria-pressed to the boolean isActive value (not a string) so the
active state reflects the current activeYear managed by setActiveYear.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
components/MemberCard.tsx (1)

19-19: Use responsive image height utilities for smaller viewports.

Line 19 uses a fixed h-64; consider responsive variants (h-48 sm:h-56 md:h-64) to improve mobile layout behavior.

As per coding guidelines, "Implement responsive design".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/MemberCard.tsx` at line 19, In the MemberCard component update the
image height utility to be responsive instead of a fixed h-64: locate the JSX
where className="w-full h-64 object-cover" (inside the MemberCard component) and
replace the single fixed height with responsive classes such as "w-full h-48
sm:h-56 md:h-64 object-cover" (or equivalent breakpoints per your design system)
so smaller viewports use a smaller height while preserving the desktop size.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/MemberCard.tsx`:
- Line 15: The card always applies pointer/hover affordances even when not
clickable; in the MemberCard component make the cursor and hover classes
conditional on the presence of linkedinUrl (or whatever prop controls
clickability). Update the className expression on the root div to include
"cursor-pointer", "hover:scale-105", "hover:bg-white/10", and
"hover:border-white/20" only when linkedinUrl is truthy (or when the card is
wrapped in an interactive element), so non-interactive cards keep the default
cursor and no hover scale/style.
- Around line 23-47: The component uses linkedinUrl directly for the anchor href
and icon rendering (linkedinUrl, MemberCard); validate the value before using it
by attempting to construct a URL (try new URL(linkedinUrl)) and ensuring the
protocol is http: or https: (reject javascript:, data:, etc.). Only render the
linked icon and the <a href=...> wrapper when the URL passes validation;
otherwise render the card without the anchor. Add the validation check near
where linkedinUrl is read and use that boolean (e.g., isValidLinkedInUrl) to
gate both the SVG icon block and the anchor return path.

---

Nitpick comments:
In `@components/MemberCard.tsx`:
- Line 19: In the MemberCard component update the image height utility to be
responsive instead of a fixed h-64: locate the JSX where className="w-full h-64
object-cover" (inside the MemberCard component) and replace the single fixed
height with responsive classes such as "w-full h-48 sm:h-56 md:h-64
object-cover" (or equivalent breakpoints per your design system) so smaller
viewports use a smaller height while preserving the desktop size.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e199f97a-ca9d-4e6c-a2ac-11576f15a321

📥 Commits

Reviewing files that changed from the base of the PR and between e3e6cee and 06ae070.

📒 Files selected for processing (2)
  • components/BayAreaYearTabs.tsx
  • components/MemberCard.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/BayAreaYearTabs.tsx

Comment thread components/MemberCard.tsx
linkedinUrl,
}: MemberCardProps) {
const content = (
<div className="relative overflow-hidden transition-all duration-300 bg-white/5 hover:bg-white/10 border border-white/10 hover:border-white/20 rounded-lg hover:scale-105 cursor-pointer group h-full">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid pointer affordance when no link is present.

Line 15 always sets cursor-pointer, but cards without linkedinUrl are not interactive. This is a small UX mismatch; make cursor/hover affordances conditional on actual clickability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/MemberCard.tsx` at line 15, The card always applies pointer/hover
affordances even when not clickable; in the MemberCard component make the cursor
and hover classes conditional on the presence of linkedinUrl (or whatever prop
controls clickability). Update the className expression on the root div to
include "cursor-pointer", "hover:scale-105", "hover:bg-white/10", and
"hover:border-white/20" only when linkedinUrl is truthy (or when the card is
wrapped in an interactive element), so non-interactive cards keep the default
cursor and no hover scale/style.

Comment thread components/MemberCard.tsx
Comment on lines +23 to +47
{linkedinUrl && (
<div className="absolute top-2 right-2">
<div className="w-8 h-8 bg-white/90 rounded-full flex items-center justify-center">
<svg className="w-4 h-4 text-[#0077b5]" fill="currentColor" viewBox="0 0 24 24">
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" />
</svg>
</div>
</div>
)}

<div className="absolute bottom-0 left-0 right-0 p-4 text-center">
<h4 className="font-bold text-white text-sm mb-1">{name}</h4>
<p className="text-pink-300 text-xs font-semibold uppercase tracking-wide">{role}</p>
</div>
</div>
)

if (linkedinUrl) {
return (
<a
href={linkedinUrl}
target="_blank"
rel="noopener noreferrer"
className="block h-full"
>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Validate linkedinUrl before using it as href.

Line 43 uses upstream data directly as a URL. If a malformed or unsafe scheme slips through (for example javascript:), it can execute on click. Gate this with strict URL validation and only render the link/icon when valid.

Proposed fix
 export default function MemberCard({
     name,
     imageUrl,
     role,
     linkedinUrl,
 }: MemberCardProps) {
+    const safeLinkedinUrl = (() => {
+        if (!linkedinUrl) return undefined
+        try {
+            const parsed = new URL(linkedinUrl)
+            const isLinkedInHost =
+                parsed.hostname === "linkedin.com" ||
+                parsed.hostname === "www.linkedin.com" ||
+                parsed.hostname.endsWith(".linkedin.com")
+            return parsed.protocol === "https:" && isLinkedInHost ? parsed.toString() : undefined
+        } catch {
+            return undefined
+        }
+    })()
+
     const content = (
         <div className="relative overflow-hidden transition-all duration-300 bg-white/5 hover:bg-white/10 border border-white/10 hover:border-white/20 rounded-lg hover:scale-105 cursor-pointer group h-full">
             <img
                 src={imageUrl}
                 alt={name}
                 className="w-full h-64 object-cover"
             />
             <div className="absolute inset-0 bg-gradient-to-t from-[`#00002c`]/60 via-[`#00002c`]/20 to-transparent"></div>
 
-            {linkedinUrl && (
+            {safeLinkedinUrl && (
                 <div className="absolute top-2 right-2">
                     <div className="w-8 h-8 bg-white/90 rounded-full flex items-center justify-center">
                         <svg className="w-4 h-4 text-[`#0077b5`]" fill="currentColor" viewBox="0 0 24 24">
@@
-    if (linkedinUrl) {
+    if (safeLinkedinUrl) {
         return (
             <a
-                href={linkedinUrl}
+                href={safeLinkedinUrl}
                 target="_blank"
                 rel="noopener noreferrer"
                 className="block h-full"
             >
                 {content}
             </a>
         )
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/MemberCard.tsx` around lines 23 - 47, The component uses
linkedinUrl directly for the anchor href and icon rendering (linkedinUrl,
MemberCard); validate the value before using it by attempting to construct a URL
(try new URL(linkedinUrl)) and ensuring the protocol is http: or https: (reject
javascript:, data:, etc.). Only render the linked icon and the <a href=...>
wrapper when the URL passes validation; otherwise render the card without the
anchor. Add the validation check near where linkedinUrl is read and use that
boolean (e.g., isValidLinkedInUrl) to gate both the SVG icon block and the
anchor return path.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
components/BayAreaYearTabs.tsx (1)

76-87: ⚠️ Potential issue | 🟡 Minor

Expose selected year state to assistive tech.

Add aria-pressed={isActive} on the year toggle button so the active selection is programmatically conveyed.

♿ Proposed fix
                         <button
                             key={year.id}
                             type="button"
                             onClick={() => setActiveYear(year.id)}
+                            aria-pressed={isActive}
                             className={`px-4 py-2 border text-sm font-bold uppercase tracking-wide transition-colors ${isActive
                                 ? 'bg-brand-pink text-white border-brand-pink'
                                 : 'bg-white/5 text-gray-300 border-white/15 hover:bg-white/10'
                                 }`}
                         >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/BayAreaYearTabs.tsx` around lines 76 - 87, The year toggle button
in BayAreaYearTabs.tsx doesn't expose its selected state to assistive tech;
update the button rendered inside the map (the element using key={year.id},
onClick={() => setActiveYear(year.id)}, and className referencing isActive) to
include aria-pressed={isActive} so the active selection is programmatically
conveyed (use the existing isActive boolean tied to year.id).
🧹 Nitpick comments (1)
components/BayAreaYearTabs.tsx (1)

109-111: Harden external links opened with target="_blank".

Use rel="noopener noreferrer" explicitly for both link blocks.

💡 Proposed fix
-                                        rel="noreferrer"
+                                        rel="noopener noreferrer"
@@
-                                                                        rel="noreferrer"
+                                                                        rel="noopener noreferrer"

Also applies to: 211-213

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/BayAreaYearTabs.tsx` around lines 109 - 111, In
BayAreaYearTabs.tsx there are anchor elements in the JSX (anchors with
target="_blank" and rel="noreferrer", e.g., the anchor that currently has
className="block h-full p-4 transition-colors hover:bg-white/[0.06]") that need
to be hardened: update the rel attribute to include "noopener" (change
rel="noreferrer" to rel="noopener noreferrer") for that anchor and the other
occurrence mentioned (the anchor around lines 211-213) so external links opened
with target="_blank" do not expose window.opener.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/BayAreaYearTabs.tsx`:
- Around line 31-33: The code currently assigns unvalidated JSON from
response.json() directly into state via setMembers, which can cause members.find
and m.name.toLowerCase to crash if the payload shape drifts; update the
fetch/handler in the BayAreaYearTabs component to validate the payload before
calling setMembers: ensure the parsed data is an array of objects and each
object has a string name (or the expected fields), only call setMembers with the
validated/normalized array, and where members is read (e.g., members.find and
m.name.toLowerCase) add a safe guard (check members is an array and m.name is a
string) or normalize items to avoid runtime errors. Use the unique symbols
response.json(), setMembers, members.find, and m.name.toLowerCase to locate and
update the code.
- Around line 26-39: The effect in useEffect that defines loadMembers should
abort any in-flight fetch when the component unmounts: create an
AbortController, pass controller.signal to fetch inside loadMembers, and return
a cleanup function that calls controller.abort(); inside the try/catch handle
the aborted fetch by skipping setMembers (e.g., detect error.name ===
'AbortError' or signal.aborted) so setMembers is not called after unmount.
Update references to loadMembers, useEffect, and setMembers accordingly.

---

Duplicate comments:
In `@components/BayAreaYearTabs.tsx`:
- Around line 76-87: The year toggle button in BayAreaYearTabs.tsx doesn't
expose its selected state to assistive tech; update the button rendered inside
the map (the element using key={year.id}, onClick={() =>
setActiveYear(year.id)}, and className referencing isActive) to include
aria-pressed={isActive} so the active selection is programmatically conveyed
(use the existing isActive boolean tied to year.id).

---

Nitpick comments:
In `@components/BayAreaYearTabs.tsx`:
- Around line 109-111: In BayAreaYearTabs.tsx there are anchor elements in the
JSX (anchors with target="_blank" and rel="noreferrer", e.g., the anchor that
currently has className="block h-full p-4 transition-colors
hover:bg-white/[0.06]") that need to be hardened: update the rel attribute to
include "noopener" (change rel="noreferrer" to rel="noopener noreferrer") for
that anchor and the other occurrence mentioned (the anchor around lines 211-213)
so external links opened with target="_blank" do not expose window.opener.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1e8b83c7-387d-44cd-aaff-b00d1b95aee4

📥 Commits

Reviewing files that changed from the base of the PR and between 155c79c and 8b540bf.

⛔ Files ignored due to path filters (3)
  • public/bayarea/logos/google-x.svg is excluded by !**/*.svg
  • public/bayarea/logos/nvidia.svg is excluded by !**/*.svg
  • public/bayarea/logos/y-combinator.svg is excluded by !**/*.svg
📒 Files selected for processing (2)
  • components/BayAreaYearTabs.tsx
  • lib/startGoesBayAreaData.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/startGoesBayAreaData.ts

Comment on lines +26 to +39
useEffect(() => {
const loadMembers = async () => {
try {
const response = await fetch('/api/members')
if (!response.ok) throw new Error('Failed to fetch members')
const data = await response.json()
setMembers(data)
} catch (error) {
console.error('Error fetching members:', error)
setMembers([])
}
}
loadMembers()
}, [])
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Abort the in-flight members request on unmount.

Without cleanup, this effect can still resolve and call state setters after unmount.

💡 Proposed fix
 useEffect(() => {
+    const controller = new AbortController()
     const loadMembers = async () => {
         try {
-            const response = await fetch('/api/members')
+            const response = await fetch('/api/members', { signal: controller.signal })
             if (!response.ok) throw new Error('Failed to fetch members')
             const data = await response.json()
             setMembers(data)
         } catch (error) {
+            if (error instanceof DOMException && error.name === 'AbortError') return
             console.error('Error fetching members:', error)
             setMembers([])
         }
     }
     loadMembers()
+    return () => controller.abort()
 }, [])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/BayAreaYearTabs.tsx` around lines 26 - 39, The effect in useEffect
that defines loadMembers should abort any in-flight fetch when the component
unmounts: create an AbortController, pass controller.signal to fetch inside
loadMembers, and return a cleanup function that calls controller.abort(); inside
the try/catch handle the aborted fetch by skipping setMembers (e.g., detect
error.name === 'AbortError' or signal.aborted) so setMembers is not called after
unmount. Update references to loadMembers, useEffect, and setMembers
accordingly.

Comment on lines +31 to +33
const data = await response.json()
setMembers(data)
} catch (error) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Validate /api/members payload before setMembers.

Line 32 stores unvalidated JSON directly; if payload shape drifts, Line 43 can crash (members.find / m.name.toLowerCase path). Add a runtime guard before committing state.

💡 Proposed fix
 interface Member {
     id: number
     name: string
     imageUrl: string
     linkedinUrl?: string
 }
+
+const isMember = (value: unknown): value is Member => {
+    if (!value || typeof value !== 'object') return false
+    const candidate = value as Partial<Member>
+    return (
+        typeof candidate.id === 'number' &&
+        typeof candidate.name === 'string' &&
+        typeof candidate.imageUrl === 'string'
+    )
+}
@@
-                const data = await response.json()
-                setMembers(data)
+                const data: unknown = await response.json()
+                setMembers(Array.isArray(data) ? data.filter(isMember) : [])

Also applies to: 43-43

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/BayAreaYearTabs.tsx` around lines 31 - 33, The code currently
assigns unvalidated JSON from response.json() directly into state via
setMembers, which can cause members.find and m.name.toLowerCase to crash if the
payload shape drifts; update the fetch/handler in the BayAreaYearTabs component
to validate the payload before calling setMembers: ensure the parsed data is an
array of objects and each object has a string name (or the expected fields),
only call setMembers with the validated/normalized array, and where members is
read (e.g., members.find and m.name.toLowerCase) add a safe guard (check members
is an array and m.name is a string) or normalize items to avoid runtime errors.
Use the unique symbols response.json(), setMembers, members.find, and
m.name.toLowerCase to locate and update the code.

Benedikt Schneidereit added 2 commits April 3, 2026 21:45
- Introduced BayAreaYearPreview component for displaying year previews.
- Created BayAreaYearTabs component to manage year-specific content and member data.
- Developed StartGoesBayAreaContent component to serve as the main content area for the Bay Area program.
- Added types for Bay Area data structures including BayAreaYearContent, BayAreaVisit, and others to ensure type safety.
- Implemented fetching of member data from an API and integrated it into the year tabs.
- Enhanced UI with responsive design and accessibility features.
Brings in latest main changes (Renovate config, hero VH fix, mobile iOS fix)
while keeping Bay Area feature additions.
Benedikt Schneidereit added 5 commits April 18, 2026 13:51
- remove old year-stamp and hero year selector UI

- align Bay Area hero style with other pages and keep two animated 20+ badges

- restore year selector with controlled scope (only year label, logos, and stats change)

- remove week/day timeline program block while keeping partners and orga team

- redesign highlights cards for consistent headings, spacing, and hover animations

- add website links to key visits, hosts cards, and below-hero logo carousel

- add compact orga team card variant and adaptive partners logo grid

- add permanent 2027 preview banner below hero area with disabled CTA

- tune preview copy/colors/date labels and reuse hero image for preview background

- reorder placeholder/logos sections and match hero spacing to Events page
Benedikt Schneidereit added 4 commits April 18, 2026 16:41
- add per-logo light/dark theme support in Bay Area data/types

- improve logo chips in hosts and carousel with stronger contrast styling

- show company names in below-hero carousel cards

- add and wire missing 2025 logos (a16z, netlify, uncork, uc berkeley)

- update 2025 hosts/visits to include logo paths and dark mode for Uncork
- wire newly added 2025 logos and links across visits/hosts

- split Pendulum and Dave Hersh into company vs person entries

- add logo theme override for Psi Quantum and keep Uncork dark

- dedupe below-hero carousel logos by logoPath

- sync Visits stat with ecosystem partner count
- Remove unused detailed visit data structures (BayAreaOverviewItem, BayAreaTimelineMilestone, BayAreaVisit, BayAreaDetailedDay, BayAreaWeekGroup types)
- Remove all detailedDays arrays from 2025, 2026, 2027 year data (~1500+ lines)
- Remove unused intro fields (timelineIntro, detailedVisitsIntro, hostsIntro, teamIntro, detailedVisitsPreviewText)
- Simplify data structure to core sections: highlightVisits, heroStats, hosts, teamMembers
- Reduce heroStats from 3 to 2 items per year (remove 'Year' label from 2025/2026; keep Status/Focus for 2027)
- Update logo carousel aggregation to use only highlightVisits and hosts (removed detailedVisitLogos)
- Improve copy in program overview section (StartGoesBayAreaContent.tsx) with more inclusive language

Files modified:
- lib/startGoesBayAreaData.ts: File reduced from ~1000+ to ~350 lines
- app/start-goes-bay-area/types.ts: Removed 5 unused interfaces
- app/start-goes-bay-area/components/StartGoesBayAreaContent.tsx: Updated overview pillar copy
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant