diff --git a/frontend/app/api/execs/route.js b/frontend/app/api/execs/route.js
new file mode 100644
index 0000000..2e6184c
--- /dev/null
+++ b/frontend/app/api/execs/route.js
@@ -0,0 +1,48 @@
+import {
+ getSupabaseCredentials,
+ hasMissingAnonCredentials,
+ requestSupabaseEdgeFunction,
+} from "../utils/supabase";
+
+export async function GET() {
+ const credentials = getSupabaseCredentials();
+
+ if (hasMissingAnonCredentials(credentials)) {
+ return Response.json(
+ {
+ error:
+ "Server misconfiguration: SUPABASE_URL and/or SUPABASE_ANON_KEY are not set.",
+ },
+ { status: 500 },
+ );
+ }
+
+ try {
+ const { response, body } = await requestSupabaseEdgeFunction(
+ credentials.url,
+ "exec",
+ credentials.anonKey,
+ );
+
+ if (!response.ok) {
+ return Response.json(
+ {
+ error:
+ body?.error ||
+ body?.message ||
+ `Upstream error from Supabase: ${response.status} ${response.statusText}`,
+ },
+ { status: response.status },
+ );
+ }
+
+ return Response.json(body);
+ } catch (error) {
+ console.error("Failed to fetch executive data:", error);
+
+ return Response.json(
+ { error: "Unable to retrieve executive data." },
+ { status: 500 },
+ );
+ }
+}
\ No newline at end of file
diff --git a/frontend/components/ExecutiveSection.tsx b/frontend/components/ExecutiveSection.tsx
index 76e1724..49e704d 100644
--- a/frontend/components/ExecutiveSection.tsx
+++ b/frontend/components/ExecutiveSection.tsx
@@ -1,246 +1,198 @@
+"use client";
+
+import { useEffect, useState } from "react";
import FlipCard from "./FlipCard";
import SectionHeader from "./SectionHeader";
+import ErrorState from "./ErrorState";
-const execTeamData = [
- {
- title: "Presidents",
- members: [
- {
- name: "Danielle Santos",
- position: "Co-President",
- description: "This person is super important for WiCSM. This person is super important for WiCSM.",
- imageUrl: "/IMG_1710.jpg",
- },
- {
- name: "Josephine Tjhia",
- position: "Co-President",
- description: "This person is super important for WiCSM. This person is super important for WiCSM.",
- imageUrl: "/IMG_1710.jpg",
- }
- ]
- },
- {
- title: "Marketing",
- members: [
- {
- name: "Vicky Zhong",
- position: "VP Marketing",
- description: "This person is super important for WiCSM. This person is super important for WiCSM.",
- imageUrl: "/IMG_1710.jpg",
- },
- {
- name: "Krisha Patel",
- position: "Director of Graphics",
- description: "This person is super important for WiCSM. This person is super important for WiCSM.",
- imageUrl: "/IMG_1710.jpg",
- },
- {
- name: "Maissara Said Bakar",
- position: "Director of Campus",
- description: "This person is super important for WiCSM. This person is super important for WiCSM.",
- imageUrl: "/IMG_1710.jpg",
- },
- {
- name: "Conrad Mo",
- position: "Director of Socials",
- description: "This person is super important for WiCSM. This person is super important for WiCSM.",
- imageUrl: "/IMG_1710.jpg",
- },
- {
- name: "Fareeha Noor",
- position: "Graphic Designer",
- description: "This person is super important for WiCSM. This person is super important for WiCSM.",
- imageUrl: "/IMG_1710.jpg",
- },
- {
- name: "Kaitlyn Qian",
- position: "Graphic Designer",
- description: "This person is super important for WiCSM. This person is super important for WiCSM.",
- imageUrl: "/IMG_1710.jpg",
- }
- ]
- },
- {
- title: "Finance",
- members: [
- {
- name: "Rayyan Anam",
- position: "VP Finance",
- description: "This person is super important for WiCSM. This person is super important for WiCSM.",
- imageUrl: "/IMG_1710.jpg",
- }
- ]
- },
- {
- title: "Events",
- members: [
- {
- name: "Jennifer Huang",
- position: "VP Events",
- description: "This person is super important for WiCSM. This person is super important for WiCSM.",
- imageUrl: "/IMG_1710.jpg",
- },
- {
- name: "Shangavi Muhunthan",
- position: "Event Coordinator",
- description: "This person is super important for WiCSM. This person is super important for WiCSM.",
- imageUrl: "/IMG_1710.jpg",
- },
- {
- name: "Shubham Garg",
- position: "Event Coordinator",
- description: "This person is super important for WiCSM. This person is super important for WiCSM.",
- imageUrl: "/IMG_1710.jpg",
- },
- {
- name: "Sylvia Xu",
- position: "Event Coordinator",
- description: "This person is super important for WiCSM. This person is super important for WiCSM.",
- imageUrl: "/IMG_1710.jpg",
- }
- ]
- },
- {
- title: "Corporations",
- members: [
- {
- name: "Maanya Gupta",
- position: "Associate",
- description: "This person is super important for WiCSM. This person is super important for WiCSM.",
- imageUrl: "/IMG_1710.jpg",
- },
- {
- name: "Kehu Li",
- position: "Associate",
- description: "This person is super important for WiCSM. This person is super important for WiCSM.",
- imageUrl: "/IMG_1710.jpg",
- },
- {
- name: "Shubhra Rashmi",
- position: "Associate",
- description: "This person is super important for WiCSM. This person is super important for WiCSM.",
- imageUrl: "/IMG_1710.jpg",
- }
- ]
- },
- {
- title: "Mentorship",
- members: [
- {
- name: "Arushi Bhatt",
- position: "VP Mentorship",
- description: "This person is super important for WiCSM. This person is super important for WiCSM.",
- imageUrl: "/IMG_1710.jpg",
- },
- {
- name: "Kayleigh Doopan",
- position: "Associate",
- description: "This person is super important for WiCSM. This person is super important for WiCSM.",
- imageUrl: "/IMG_1710.jpg",
- }
- ]
- },
- {
- title: "First Years",
- members: [
- {
- name: "Rose Gu",
- position: "First Year Rep",
- description: "This person is super important for WiCSM. This person is super important for WiCSM.",
- imageUrl: "/IMG_1710.jpg",
- },
- {
- name: "Ire Oluyemi",
- position: "First Year Rep",
- description: "This person is super important for WiCSM. This person is super important for WiCSM.",
- imageUrl: "/IMG_1710.jpg",
- },
- {
- name: "Thanusa Paskaran",
- position: "First Year Rep",
- description: "This person is super important for WiCSM. This person is super important for WiCSM.",
- imageUrl: "/IMG_1710.jpg",
- }
- ]
- }
+async function getExecs() {
+ const response = await fetch("/api/execs");
+ const data = await response.json().catch(() => ({}));
+
+ if (!response.ok) {
+ throw new Error(data.error || "Failed to fetch executive data");
+ }
+
+ if (!Array.isArray(data.execs)) {
+ throw new Error("Executive data was returned in an invalid format");
+ }
+
+ return data.execs;
+}
+
+const SECTION_CONFIG = [
+ { title: "Presidents", orderIndexes: [[1, 2]] },
+ { title: "Marketing", orderIndexes: [[3, 11]] },
+ { title: "Events", orderIndexes: [[12, 15]] },
+ { title: "Corporations", orderIndexes: [[16, 18]] },
+ { title: "Mentorship", orderIndexes: [[19, 20]] },
+ { title: "Finance", orderIndexes: [[21, 21]] },
];
-export default function ExecutiveSection() {
+function orderIndexInRanges(orderIndex, ranges) {
+ return ranges.some(
+ ([start, end]) => orderIndex >= start && orderIndex <= end,
+ );
+}
+
+function groupExecs(execs) {
+ const assignedIds = new Set();
+
+ const sections = SECTION_CONFIG.map(({ title, orderIndexes }) => {
+ const members = execs.filter((member) => {
+ if (orderIndexInRanges(member.order_index, orderIndexes)) {
+ assignedIds.add(member.id);
+ return true;
+ }
+
+ return false;
+ });
+
+ return {
+ title,
+ members,
+ };
+ }).filter((section) => section.members.length > 0);
+
+ const remainingMembers = execs.filter(
+ (member) => !assignedIds.has(member.id),
+ );
+
+ if (remainingMembers.length > 0) {
+ sections.push({
+ title: "Executive Team",
+ members: remainingMembers,
+ });
+ }
+
+ return sections;
+}
+
+function splitByRole(members) {
+ const vicePresidents = members.filter((member) => {
+ const role = member.role ?? "";
+
return (
-
- Meet the amazing people at WiCSM!
-
- {team.title}
-
-
- {/* VPs/Co-Prez row */}
- {vpMembers.length > 0 && (
-
+ Meet the amazing people at WiCSM! +
+ + {loading && ( ++ Loading team... +
+ )} + + {!loading && error && ( +