From fab19fd6c71e6b7a6fb2ed5273e2907958371bd6 Mon Sep 17 00:00:00 2001 From: Ethan Seiz Date: Tue, 26 May 2026 15:18:36 -0400 Subject: [PATCH 1/7] prep for Vercel deploy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - fix wrong import path in StaffCard/UndergradCard (TeamMember lives in CosmicTypes, not CosmicServices) — production build failed without this - update next.config.ts to use remotePatterns and include cdn.cosmicjs.com (the imgix.cosmicjs.com fallback URL) - track .env.example so contributors know which env vars to set Co-Authored-By: Claude Opus 4.7 (1M context) --- .env.example | 5 +++++ .gitignore | 1 + components/StaffCard.tsx | 2 +- components/UndergradCard.tsx | 2 +- next.config.ts | 6 ++++-- 5 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..65ad0d4 --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +# Cosmic CMS – get these from your Cosmic bucket dashboard +# https://app.cosmicjs.com → your bucket → Settings → API Keys +COSMIC_BUCKET_SLUG=your-bucket-slug +COSMIC_READ_KEY=your-read-key +COSMIC_WRITE_KEY=your-write-key diff --git a/.gitignore b/.gitignore index e25f3d5..d084156 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ yarn-error.log* # env files (can opt-in for committing if needed) .env* +!.env.example # vercel .vercel diff --git a/components/StaffCard.tsx b/components/StaffCard.tsx index 8427981..db7bc2b 100644 --- a/components/StaffCard.tsx +++ b/components/StaffCard.tsx @@ -1,4 +1,4 @@ -import { TeamMember } from "@/services/CosmicServices"; +import { TeamMember } from "@/services/CosmicTypes"; type Props = { member: TeamMember; diff --git a/components/UndergradCard.tsx b/components/UndergradCard.tsx index a15e1c3..8f75174 100644 --- a/components/UndergradCard.tsx +++ b/components/UndergradCard.tsx @@ -1,4 +1,4 @@ -import { TeamMember } from "@/services/CosmicServices"; +import { TeamMember } from "@/services/CosmicTypes"; type Props = { member: TeamMember; diff --git a/next.config.ts b/next.config.ts index 609830b..5a1424d 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,9 +1,11 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ images: { - domains: ['imgix.cosmicjs.com'], + remotePatterns: [ + { protocol: "https", hostname: "imgix.cosmicjs.com" }, + { protocol: "https", hostname: "cdn.cosmicjs.com" }, + ], }, }; From 88ade4c07f64eb2f8510359b36076b41c07ac1e7 Mon Sep 17 00:00:00 2001 From: Ethan Seiz Date: Tue, 26 May 2026 16:12:26 -0400 Subject: [PATCH 2/7] add Instagram feed to homepage News section via Behold - fetch latest posts from Behold JSON feed (public URL, no secret) - show first 3 posts when available; fall back to existing placeholders otherwise - restructure InstagramPostCard: image on top, username+date row below, drop fake likes/heart/comment icons - date replaces likes since Behold doesn't expose like counts - for VIDEO posts use thumbnailUrl (Behold's mediaUrl is the .mp4) - delete unused duplicate at app/components/InstagramPostCard.tsx Feed URL configured via NEXT_PUBLIC_BEHOLD_FEED_URL. Documented in .env.example. Co-Authored-By: Claude Opus 4.7 (1M context) --- .env.example | 4 + app/components/InstagramPostCard.tsx | 132 --------------------------- app/page.tsx | 61 ++++++++++--- components/InstagramPostCard.tsx | 80 +++++----------- 4 files changed, 78 insertions(+), 199 deletions(-) delete mode 100644 app/components/InstagramPostCard.tsx diff --git a/.env.example b/.env.example index 65ad0d4..4fdd47d 100644 --- a/.env.example +++ b/.env.example @@ -3,3 +3,7 @@ COSMIC_BUCKET_SLUG=your-bucket-slug COSMIC_READ_KEY=your-read-key COSMIC_WRITE_KEY=your-write-key + +# Behold.so Instagram feed – public URL, no secret. Drives the homepage News & Announcements section. +# Find the feed URL in your Behold dashboard: https://app.behold.so/ +NEXT_PUBLIC_BEHOLD_FEED_URL=https://feeds.behold.so/your-feed-id diff --git a/app/components/InstagramPostCard.tsx b/app/components/InstagramPostCard.tsx deleted file mode 100644 index ed49849..0000000 --- a/app/components/InstagramPostCard.tsx +++ /dev/null @@ -1,132 +0,0 @@ -type Props = { - imageUrl?: string; - username?: string; - caption?: string; - likes?: number; - postUrl?: string; -}; - -export default function InstagramPostCard({ - imageUrl, - username = "mindmoralitylab", - caption = "", - likes = 0, - postUrl = "#", -}: Props) { - return ( -
- {/* Header */} -
-
- M -
- {username} - - - - - - - -
- - {/* Image */} -
- {imageUrl ? ( - Instagram post - ) : ( -
- - - - - -
- )} -
- - {/* Actions */} -
- - - - - - - - - - -
- - {/* Likes */} - {likes > 0 && ( -

- {likes.toLocaleString()} likes -

- )} - - {/* Caption */} - {caption && ( -

- {username} - {caption} -

- )} - - {/* View on Instagram */} - - View on Instagram - -
- ); -} diff --git a/app/page.tsx b/app/page.tsx index 1e442b5..453c2bf 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -17,6 +17,23 @@ const page_to_link = new Map([ ["students", "/get-involved/students"], ]); +type BeholdPost = { + id: string; + timestamp: string; + permalink: string; + mediaType: "IMAGE" | "VIDEO" | "CAROUSEL_ALBUM"; + mediaUrl: string; + thumbnailUrl?: string | null; + caption?: string; +}; + +const formatPostDate = (iso: string) => + new Date(iso).toLocaleDateString("en-US", { + year: "numeric", + month: "long", + day: "numeric", + }); + export default function Home() { const slides = ["/slideshow1.png", "/slideshow1.png", "/slideshow1.png"]; // Clone of first slide appended so we can slide into it, then snap back invisibly @@ -26,6 +43,8 @@ export default function Home() { const [loading, setLoading] = useState(true); const [error, setError] = useState(""); + const [instagramPosts, setInstagramPosts] = useState(null); + const [current, setCurrent] = useState(0); const [animated, setAnimated] = useState(true); @@ -47,6 +66,15 @@ export default function Home() { fetchData(); }, []); + useEffect(() => { + const feedUrl = process.env.NEXT_PUBLIC_BEHOLD_FEED_URL; + if (!feedUrl) return; + fetch(feedUrl) + .then((r) => (r.ok ? r.json() : null)) + .then((data) => setInstagramPosts(data?.posts ?? null)) + .catch(() => setInstagramPosts(null)); + }, []); + useEffect(() => { const interval = setInterval(() => { setCurrent((prev) => prev + 1); @@ -148,18 +176,27 @@ export default function Home() { News & Announcements
- - - + {instagramPosts && instagramPosts.length >= 3 ? ( + instagramPosts.slice(0, 3).map((post) => ( + + )) + ) : ( + <> + + + + + )}
diff --git a/components/InstagramPostCard.tsx b/components/InstagramPostCard.tsx index 67e4486..717603c 100644 --- a/components/InstagramPostCard.tsx +++ b/components/InstagramPostCard.tsx @@ -2,7 +2,7 @@ type Props = { imageUrl?: string; username?: string; caption?: string; - likes?: number; + date?: string; postUrl?: string; }; @@ -10,32 +10,11 @@ export default function InstagramPostCard({ imageUrl, username = "mindmoralitylab", caption = "", - likes = 0, + date, postUrl = "#", }: Props) { return (
- - {/* Header */} -
-
- M -
- {username} - - - - - - - -
- {/* Image */}
{imageUrl ? ( @@ -55,45 +34,36 @@ export default function InstagramPostCard({ )}
- {/* Actions */} -
- - - - - - - - - - + {/* User + date row */} +
+
+ M +
+
+ {username} + {date && {date}} +
+ + + + + + +
- {/* Likes */} - {likes > 0 && ( -

- {likes.toLocaleString()} likes -

- )} - {/* Caption */} {caption && ( -

- {username} +

{caption}

)} - - {/* View on Instagram */} - - View on Instagram - -
); } From fab2784509529e8f91795b23699d2c76c0402280 Mon Sep 17 00:00:00 2001 From: Ethan Seiz Date: Tue, 26 May 2026 17:42:25 -0400 Subject: [PATCH 3/7] wire homepage slideshow to Cosmic instead of hardcoded image Was rendering /slideshow1.png three times from public/, ignoring home.metadata.slideshow_images entirely. Now uses the existing component (previously imported but unused) fed directly by Cosmic. - removes ~50 lines of inline slide-clone-and-snap-back state machinery from app/page.tsx - updates Slideshow dot colors from generic teal-500/gray-300 to brand #F2AD3D / #459A9F so the visual matches the prior inline version Adding/removing slideshow images in Cosmic now updates the site without code changes. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/page.tsx | 58 +--------------------------------------- components/Slideshow.tsx | 2 +- 2 files changed, 2 insertions(+), 58 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 453c2bf..b44cd23 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -35,19 +35,12 @@ const formatPostDate = (iso: string) => }); export default function Home() { - const slides = ["/slideshow1.png", "/slideshow1.png", "/slideshow1.png"]; - // Clone of first slide appended so we can slide into it, then snap back invisibly - const extendedSlides = [...slides, slides[0]]; - const [home, setHome] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); const [instagramPosts, setInstagramPosts] = useState(null); - const [current, setCurrent] = useState(0); - const [animated, setAnimated] = useState(true); - useEffect(() => { const fetchData = async () => { try { @@ -75,34 +68,6 @@ export default function Home() { .catch(() => setInstagramPosts(null)); }, []); - useEffect(() => { - const interval = setInterval(() => { - setCurrent((prev) => prev + 1); - }, 4000); - return () => clearInterval(interval); - }, []); - - useEffect(() => { - if (current === extendedSlides.length - 1) { - // After sliding into the clone, snap back to real first slide without animation - const timer = setTimeout(() => { - setAnimated(false); - setCurrent(0); - }, 700); - return () => clearTimeout(timer); - } - }, [current]); - - useEffect(() => { - if (!animated) { - // Re-enable animation after the snap - const timer = setTimeout(() => setAnimated(true), 50); - return () => clearTimeout(timer); - } - }, [animated]); - - const activeIndex = current === extendedSlides.length - 1 ? 0 : current; - if (loading) { return (
@@ -130,28 +95,7 @@ export default function Home() { `}
-
-
- {extendedSlides.map((slide, i) => ( -
- Lab photo -
- ))} -
- -
- {slides.map((_, i) => ( -
-
+
diff --git a/components/Slideshow.tsx b/components/Slideshow.tsx index 1e57fdf..b3ca862 100644 --- a/components/Slideshow.tsx +++ b/components/Slideshow.tsx @@ -58,7 +58,7 @@ export default function Slideshow({ images }: SlideshowProps) {
From f3319c236a71c6d34271bf2eccffbf73ab22dc80 Mon Sep 17 00:00:00 2001 From: Ethan Seiz Date: Wed, 27 May 2026 12:08:47 -0400 Subject: [PATCH 5/7] Add GitHub Actions workflows for Vercel deploys --- .github/workflows/preview.yaml | 21 +++++++++++++++++++++ .github/workflows/production.yaml | 21 +++++++++++++++++++++ vercel.json | 5 +++++ 3 files changed, 47 insertions(+) create mode 100644 .github/workflows/preview.yaml create mode 100644 .github/workflows/production.yaml create mode 100644 vercel.json diff --git a/.github/workflows/preview.yaml b/.github/workflows/preview.yaml new file mode 100644 index 0000000..221656e --- /dev/null +++ b/.github/workflows/preview.yaml @@ -0,0 +1,21 @@ +name: Vercel Preview Deployment +env: + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} +on: + push: + branches-ignore: + - main +jobs: + Deploy-Preview: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Vercel CLI + run: npm install --global vercel@latest + - name: Pull Vercel Environment Information + run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }} + - name: Build Project Artifacts + run: vercel build --token=${{ secrets.VERCEL_TOKEN }} + - name: Deploy Project Artifacts to Vercel + run: vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }} diff --git a/.github/workflows/production.yaml b/.github/workflows/production.yaml new file mode 100644 index 0000000..aef62e8 --- /dev/null +++ b/.github/workflows/production.yaml @@ -0,0 +1,21 @@ +name: Vercel Production Deployment +env: + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} +on: + push: + branches: + - main +jobs: + Deploy-Production: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Vercel CLI + run: npm install --global vercel@latest + - name: Pull Vercel Environment Information + run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }} + - name: Build Project Artifacts + run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }} + - name: Deploy Project Artifacts to Vercel + run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }} diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..45c873b --- /dev/null +++ b/vercel.json @@ -0,0 +1,5 @@ +{ + "git": { + "deploymentEnabled": false + } +} From 3d1084724178a10893fc2072c19e66673a97b32c Mon Sep 17 00:00:00 2001 From: Vincent <07xuvi@gmail.com> Date: Wed, 3 Jun 2026 23:16:25 -0400 Subject: [PATCH 6/7] Add Cosmic keys and Next.js feed URL to preview workflow Added environment variables for Cosmic and Next.js feed URL. --- .github/workflows/preview.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/preview.yaml b/.github/workflows/preview.yaml index 221656e..ec528a8 100644 --- a/.github/workflows/preview.yaml +++ b/.github/workflows/preview.yaml @@ -2,6 +2,10 @@ name: Vercel Preview Deployment env: VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} + COSMIC_BUCKET_SLUG: ${{ secrets.COSMIC_BUCKET_SLUG }} + COSMIC_READ_KEY: ${{ secrets.COSMIC_READ_KEY }} + COSMIC_WRITE_KEY: ${{ secrets.COSMIC_WRITE_KEY }} + NEXT_PUBLIC_BEHOLD_FEED_URL: ${{ secrets.NEXT_PUBLIC_BEHOLD_FEED_URL }} on: push: branches-ignore: From 381048d84c23aa1cbd19d8bfcab06acd98a9705a Mon Sep 17 00:00:00 2001 From: Vincent <07xuvi@gmail.com> Date: Wed, 3 Jun 2026 23:16:42 -0400 Subject: [PATCH 7/7] Add Cosmic environment variables to production workflow --- .github/workflows/production.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/production.yaml b/.github/workflows/production.yaml index aef62e8..b207f8c 100644 --- a/.github/workflows/production.yaml +++ b/.github/workflows/production.yaml @@ -2,6 +2,10 @@ name: Vercel Production Deployment env: VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} + COSMIC_BUCKET_SLUG: ${{ secrets.COSMIC_BUCKET_SLUG }} + COSMIC_READ_KEY: ${{ secrets.COSMIC_READ_KEY }} + COSMIC_WRITE_KEY: ${{ secrets.COSMIC_WRITE_KEY }} + NEXT_PUBLIC_BEHOLD_FEED_URL: ${{ secrets.NEXT_PUBLIC_BEHOLD_FEED_URL }} on: push: branches: