Files
spsays-training/lib/book-reference.ts
T

173 lines
4.8 KiB
TypeScript

/**
* Book + chapter derivation for transcriptions.
*
* Transcriptions carry a free-text scripture `reference` (e.g. "SB 1.2.3",
* "BG Introduction", "CC Madhya-līlā 20.101") rather than structured
* book/chapter columns. This module is the single source of truth for turning
* those strings into a book identity and a chapter grouping, reused by the
* books grid, the listing page, and the book browser.
*
* The book registry mirrors `TranscriptionBooks` in the Go BFF
* (internal/repository/transcription_repo.go).
*/
export type BookCode = "SB" | "BG" | "CC" | "BS" | "NOD" | "ISO";
export const BOOKS: Record<BookCode, { title: string; order: number }> = {
SB: { title: "Śrīmad-Bhāgavatam", order: 1 },
BG: { title: "Bhagavad-gītā", order: 2 },
CC: { title: "Caitanya-caritāmṛta", order: 3 },
BS: { title: "Brahma-saṁhitā", order: 4 },
NOD: { title: "The Nectar of Devotion", order: 5 },
ISO: { title: "Śrī Īśopaniṣad", order: 6 },
};
/** Display title for a (possibly unknown) book code; "" if unknown. */
export function bookTitle(code: string): string {
const b = BOOKS[code.toUpperCase() as BookCode];
return b ? b.title : "";
}
/** A section is a canto (SB) or līlā (CC); null for books without that level. */
export type ChapterSection = {
key: string;
label: string;
order: number;
};
/** A chapter grouping for the accordion list. */
export type ChapterGroup = {
key: string;
label: string;
sort: [number, number];
/** Canto/līlā this chapter belongs to (SB, CC); null for flat books. */
section: ChapterSection | null;
};
// Caitanya-caritāmṛta līlā ordering (Ādi → Madhya → Antya).
const LILA_ORDER: Record<string, { label: string; order: number }> = {
ADI: { label: "Ādi-līlā", order: 1 },
MADHYA: { label: "Madhya-līlā", order: 2 },
ANTYA: { label: "Antya-līlā", order: 3 },
};
const OTHER: ChapterGroup = {
key: "other",
label: "Other",
sort: [9999, 0],
section: null,
};
/**
* Parse a transcription reference into its chapter group, given the book code.
* Tolerates the corpus's noise (en-dash vs hyphen, "Madhya" vs "Madhya-līlā",
* a stray "SB 7", etc.). Unparseable / missing references fall into "Other".
*/
export function parseChapter(code: string, reference: string | null): ChapterGroup {
const ref = (reference ?? "").trim();
if (!ref) return OTHER;
switch (code.toUpperCase() as BookCode) {
case "BG": {
if (/^BG\s+Introduction/i.test(ref)) {
return {
key: "bg-intro",
label: "Introduction",
sort: [0, 0],
section: null,
};
}
const m = ref.match(/^BG\s+(\d+)/);
if (m) {
const n = Number(m[1]);
return {
key: `bg-${n}`,
label: `Chapter ${n}`,
sort: [n, 0],
section: null,
};
}
return OTHER;
}
case "SB": {
const m = ref.match(/^SB\s+(\d+)\.(\d+)/);
if (m) {
const c = Number(m[1]);
const ch = Number(m[2]);
return {
key: `sb-${c}-${ch}`,
label: `Chapter ${ch}`,
sort: [c, ch],
section: { key: `sb-${c}`, label: `Canto ${c}`, order: c },
};
}
const cantoOnly = ref.match(/^SB\s+(\d+)\b/);
if (cantoOnly) {
const c = Number(cantoOnly[1]);
return {
key: `sb-${c}-0`,
label: "Other",
sort: [c, 0],
section: { key: `sb-${c}`, label: `Canto ${c}`, order: c },
};
}
return OTHER;
}
case "CC": {
const m = ref.match(/^CC\s+(Ādi|Adi|Madhya|Antya)(?:-līlā)?\s+(\d+)/i);
if (m) {
const lila = LILA_ORDER[m[1].toUpperCase().replace("Ā", "A")];
const ch = Number(m[2]);
if (lila) {
return {
key: `cc-${lila.order}-${ch}`,
label: `Chapter ${ch}`,
sort: [lila.order, ch],
section: {
key: `cc-${lila.order}`,
label: lila.label,
order: lila.order,
},
};
}
}
return OTHER;
}
case "BS": {
const m = ref.match(/^BS\s+(\d+)/);
if (m) {
const n = Number(m[1]);
return {
key: `bs-${n}`,
label: `Chapter ${n}`,
sort: [n, 0],
section: null,
};
}
return OTHER;
}
case "ISO": {
const m = ref.match(/^ISO\s+Mantra\s+(\d+)/i);
if (m) {
const n = Number(m[1]);
return {
key: `iso-${n}`,
label: `Mantra ${n}`,
sort: [n, 0],
section: null,
};
}
return { key: "iso", label: "Śrī Īśopaniṣad", sort: [0, 0], section: null };
}
case "NOD":
return {
key: "nod",
label: "The Nectar of Devotion",
sort: [0, 0],
section: null,
};
default:
return OTHER;
}
}