- {settings.photos.length > 0 && (
-
- {settings.photos.map((p, i) => (
- // eslint-disable-next-line @next/next/no-img-element
-
})
- ))}
-
+ {[settings.photos.filter((p) => p.row !== 2), settings.photos.filter((p) => p.row === 2)].map(
+ (row, r) =>
+ row.length > 0 && (
+
+ {row.map((p, i) => (
+ // eslint-disable-next-line @next/next/no-img-element
+
})
+ ))}
+
+ ),
)}
{admin && (
diff --git a/lib/board/chapters.ts b/lib/board/chapters.ts
index 46de12a..376b41b 100644
--- a/lib/board/chapters.ts
+++ b/lib/board/chapters.ts
@@ -35,6 +35,8 @@ export type ChapterConfig = {
quickLinks: { label?: string; text: string; href?: string | null; tab?: Tab }[];
keyDates: [date: string, text: string][];
photos: string[];
+ // A second row of photos under the first (optional).
+ photosRow2?: string[];
socials: {
heading: string;
kinds: { key: "b" | "o"; label: string }[];
@@ -54,6 +56,9 @@ export type ChapterConfig = {
myPage: { meetingLine: string | null; teamTrack: Record; teamNote: Record };
};
+// UCLA's second photo row (added Oct 15).
+export const UCLA_ROW2 = [7, 8, 9, 10, 11, 12, 13, 14].map((n) => `/board/ucla/board-${n}.webp`);
+
const shortDay = (iso: string, o: Intl.DateTimeFormatOptions) => {
const [y, m, d] = iso.split("-").map(Number);
return new Date(y, m - 1, d).toLocaleDateString("en-US", o);
@@ -167,6 +172,7 @@ const UCLA: ChapterConfig = {
["2026-11-22", "Crosscheck demo and research writeups (week 10)"],
],
photos: [1, 2, 3, 4, 5, 6].map((n) => `/board/ucla/board-${n}.webp`),
+ photosRow2: UCLA_ROW2,
socials: {
heading: "Fall 2026 Events",
kinds: [
diff --git a/lib/board/settings.ts b/lib/board/settings.ts
index 26d5f4a..be99da2 100644
--- a/lib/board/settings.ts
+++ b/lib/board/settings.ts
@@ -7,7 +7,9 @@ import { TABS, type Tab } from "./types";
export type QuickLink = { label: string; text: string; href: string | null; tab: Tab | "" };
export type KeyDate = { date: string; text: string };
-export type Photo = { src: string };
+// `row: 2` puts a photo in a second row under the first, so adding people
+// never shrinks the photos already in the first row.
+export type Photo = { src: string; row?: 2 };
export type Projects = ChapterConfig["projects"];
export type Settings = {
@@ -48,7 +50,7 @@ export function defaultSettings(chapter: ChapterId): Settings {
.filter((l) => l !== meeting)
.map((l) => ({ label: l.label ?? "", text: l.text, href: l.href ?? null, tab: l.tab ?? "" })),
keyDates: c.keyDates.map(([date, text]) => ({ date, text })),
- photos: c.photos.map((src) => ({ src })),
+ photos: [...c.photos.map((src) => ({ src })), ...(c.photosRow2 ?? []).map((src) => ({ src, row: 2 as const }))],
projects: c.projects,
};
}
@@ -151,8 +153,9 @@ export function checkSetting(key: SettingKey, v: unknown): Ok {
return { ok: true, value: out };
}
case "photos": {
- if (!Array.isArray(v) || v.length > 12 || !v.every((p) => p && typeof p.src === "string" && isPhotoSrc(p.src))) return fail("Check the photos.");
- return { ok: true, value: v.map((p) => ({ src: p.src })) };
+ if (!Array.isArray(v) || v.length > 24 || !v.every((p) => p && typeof p.src === "string" && isPhotoSrc(p.src) && (p.row === undefined || p.row === 1 || p.row === 2)))
+ return fail("Check the photos.");
+ return { ok: true, value: v.map((p) => (p.row === 2 ? { src: p.src, row: 2 as const } : { src: p.src })) };
}
case "projects":
return projects(v);
diff --git a/lib/board/setup.ts b/lib/board/setup.ts
index 508413f..4149da3 100644
--- a/lib/board/setup.ts
+++ b/lib/board/setup.ts
@@ -6,6 +6,7 @@ import starter from "./starter-data.json";
import ucla from "./starter-ucla.json";
import { seedSettings } from "./settings-server";
import { CHAPTER_SERVER } from "./chapter-server";
+import { UCLA_ROW2 } from "./chapters";
// Makes the board database ready on first use, so nobody has to run
// commands: creates the tables (scripts/board-schema.sql, safe to re-run)
@@ -70,6 +71,23 @@ async function setup() {
await markHubAdmins(sql);
await renameEvanceline(sql);
await adminsOnlyToHubAdmins(sql);
+ await uclaPhotoRow2(sql);
+}
+
+// Adds UCLA's second photo row under the existing photos, once. The first
+// row is left exactly as it is.
+async function uclaPhotoRow2(sql: ReturnType) {
+ await sql.begin(async (tx) => {
+ const [first] = await tx`INSERT INTO board_changes (name) VALUES ('2026-10-15-ucla-photo-row-2')
+ ON CONFLICT DO NOTHING RETURNING name`;
+ if (!first) return;
+ const [row] = await tx`SELECT value FROM board_settings WHERE chapter = 'ucla' AND key = 'photos'`;
+ const photos = (row?.value ?? []) as { src: string; row?: 2 }[];
+ const have = new Set(photos.map((p) => p.src));
+ const added = UCLA_ROW2.filter((src) => !have.has(src)).map((src) => ({ src, row: 2 as const }));
+ if (!row || added.length === 0) return;
+ await tx`UPDATE board_settings SET value = ${tx.json([...photos, ...added] as never)} WHERE chapter = 'ucla' AND key = 'photos'`;
+ });
}
// Announcements marked "admins only" before "Send to" existed are for Hub
diff --git a/public/board/ucla/board-10.webp b/public/board/ucla/board-10.webp
new file mode 100644
index 0000000..0ef788a
Binary files /dev/null and b/public/board/ucla/board-10.webp differ
diff --git a/public/board/ucla/board-11.webp b/public/board/ucla/board-11.webp
new file mode 100644
index 0000000..7e33c5b
Binary files /dev/null and b/public/board/ucla/board-11.webp differ
diff --git a/public/board/ucla/board-12.webp b/public/board/ucla/board-12.webp
new file mode 100644
index 0000000..8b71688
Binary files /dev/null and b/public/board/ucla/board-12.webp differ
diff --git a/public/board/ucla/board-13.webp b/public/board/ucla/board-13.webp
new file mode 100644
index 0000000..9352e95
Binary files /dev/null and b/public/board/ucla/board-13.webp differ
diff --git a/public/board/ucla/board-14.webp b/public/board/ucla/board-14.webp
new file mode 100644
index 0000000..faa6192
Binary files /dev/null and b/public/board/ucla/board-14.webp differ
diff --git a/public/board/ucla/board-7.webp b/public/board/ucla/board-7.webp
new file mode 100644
index 0000000..56350f0
Binary files /dev/null and b/public/board/ucla/board-7.webp differ
diff --git a/public/board/ucla/board-8.webp b/public/board/ucla/board-8.webp
new file mode 100644
index 0000000..73a3e85
Binary files /dev/null and b/public/board/ucla/board-8.webp differ
diff --git a/public/board/ucla/board-9.webp b/public/board/ucla/board-9.webp
new file mode 100644
index 0000000..e45bf7a
Binary files /dev/null and b/public/board/ucla/board-9.webp differ