88 lines
2.6 KiB
TypeScript
88 lines
2.6 KiB
TypeScript
/**
|
|
* Groups the quiz list for the "All" tab:
|
|
*
|
|
* Books → one group per book, in the library's order
|
|
* Lectures & Conversations → one group per year, oldest first, undated last
|
|
* Other quizzes → stand-alone quizzes
|
|
*
|
|
* Only groups with quizzes in them are returned, so an empty category or
|
|
* sub-category never renders.
|
|
*/
|
|
|
|
import { BOOK_NAMES } from "@/lib/constants";
|
|
import type { QuizCard } from "@/lib/quiz";
|
|
|
|
export type QuizGroup = { key: string; label: string; quizzes: QuizCard[] };
|
|
|
|
export type QuizSections = {
|
|
books: QuizGroup[];
|
|
lectures: QuizGroup[];
|
|
other: QuizCard[];
|
|
};
|
|
|
|
const BOOK_ORDER = Object.keys(BOOK_NAMES);
|
|
|
|
/** "bg" from /books/bg/2 — the book a chapter quiz belongs to. */
|
|
function bookCode(path?: string | null): string | null {
|
|
const m = path?.match(/^\/books\/([^/]+)/);
|
|
return m ? m[1] : null;
|
|
}
|
|
|
|
/**
|
|
* The recording's year. Saved on the quiz since target_year existed; for
|
|
* older quizzes, read from the slug's yymmdd prefix (720720sb-london → 1972)
|
|
* when it falls in Śrīla Prabhupāda's recorded years.
|
|
*/
|
|
function lectureYear(q: QuizCard): number | null {
|
|
if (q.target_year) return q.target_year;
|
|
const m = q.target_path?.match(/^\/transcriptions\/(\d{2})\d{4}/);
|
|
if (!m) return null;
|
|
const y = 1900 + Number(m[1]);
|
|
return y >= 1966 && y <= 1977 ? y : null;
|
|
}
|
|
|
|
export function groupQuizzes(cards: QuizCard[]): QuizSections {
|
|
const books = new Map<string, QuizCard[]>();
|
|
const years = new Map<number | null, QuizCard[]>();
|
|
const other: QuizCard[] = [];
|
|
|
|
for (const q of cards) {
|
|
if (q.target_kind === "chapter") {
|
|
const code = bookCode(q.target_path) ?? "other";
|
|
books.set(code, [...(books.get(code) ?? []), q]);
|
|
} else if (
|
|
q.target_kind === "lecture" ||
|
|
q.target_kind === "conversation"
|
|
) {
|
|
const y = lectureYear(q);
|
|
years.set(y, [...(years.get(y) ?? []), q]);
|
|
} else {
|
|
other.push(q);
|
|
}
|
|
}
|
|
|
|
const rank = (code: string) => {
|
|
const i = BOOK_ORDER.indexOf(code);
|
|
return i < 0 ? BOOK_ORDER.length : i;
|
|
};
|
|
|
|
return {
|
|
books: [...books.entries()]
|
|
.sort(([a], [b]) => rank(a) - rank(b) || a.localeCompare(b))
|
|
.map(([code, quizzes]) => ({
|
|
key: code,
|
|
label: BOOK_NAMES[code] ?? code.toUpperCase(),
|
|
quizzes,
|
|
})),
|
|
lectures: [...years.entries()]
|
|
// Oldest first; undated (null) last.
|
|
.sort(([a], [b]) => (a ?? 9999) - (b ?? 9999))
|
|
.map(([year, quizzes]) => ({
|
|
key: String(year ?? "undated"),
|
|
label: year ? String(year) : "Undated",
|
|
quizzes,
|
|
})),
|
|
other,
|
|
};
|
|
}
|