/** * Menaion view — browse the daily Menaion by month and day. * Two-step selector: month → day grid → section list. */ import { assembleService, type ServiceContext } from '../core/service-assembler'; import type { EvalContext, ServiceNode } from '../core/types'; import { getTranslations, type LanguageCode } from '../core/i18n'; import { loadSettings, fontClass } from './settings-view'; export class MenaionView { private container: HTMLElement; private language: LanguageCode; private t: ReturnType; private index: Record = {}; constructor(container: HTMLElement, language: LanguageCode) { this.container = container; this.language = language; this.t = getTranslations(language); } private getFontClass(): string { const settings = loadSettings(); return (this.language === 'cu' || this.language === 'ru') && settings.cuFont !== undefined ? fontClass(settings.cuFont) : ''; } private getMonthNames(): string[] { return this.t.calendar.monthsGenitive; } async render() { try { const resp = await fetch('/data/shared/menaion-daily/index.json'); if (resp.ok) { this.index = await resp.json() as Record; } } catch { /* empty */ } const months = this.getMonthNames(); const daysInMonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; const today = new Date(); // Count sections per month const monthCounts: number[] = new Array(12).fill(0); for (const [dk, n] of Object.entries(this.index)) { const m = parseInt(dk.slice(0, 2), 10) - 1; if (m >= 0 && m < 12) monthCounts[m] += n; } const monthHtml = months.map((m, i) => ` `).join(''); this.container.innerHTML = `

${this.t.menaion.title}

${months[0]}

${monthHtml}

${this.t.prayer.selectSection}

`; this.container.querySelectorAll('.menaion-month').forEach(btn => { btn.addEventListener('click', () => { const month = parseInt(btn.getAttribute('data-month')!, 10); this.renderMonth(month, daysInMonth[month - 1]); }); }); // Default: current month this.renderMonth(today.getMonth() + 1, daysInMonth[today.getMonth()]); } private renderMonth(month: number, maxDay: number) { const daysEl = this.container.querySelector('#menaion-days'); if (!daysEl) return; const mm = String(month).padStart(2, '0'); const days = []; for (let d = 1; d <= maxDay; d++) { const dd = String(d).padStart(2, '0'); const key = `${mm}-${dd}`; const n = this.index[key]; days.push(` `); } daysEl.innerHTML = days.join(''); this.container.querySelectorAll('.menaion-day:not([disabled])').forEach(btn => { btn.addEventListener('click', () => { this.loadDay(btn.getAttribute('data-date')!); }); }); } private async loadDay(dateKey: string) { const contentEl = this.container.querySelector('#menaion-content') as HTMLElement | null; if (!contentEl) return; contentEl.innerHTML = `

${this.t.loading}

`; const count = this.index[dateKey] ?? 0; let html = ''; for (let i = 1; i <= count; i++) { try { const resp = await fetch(`/data/shared/menaion-daily/${dateKey}/${i}.json`); if (!resp.ok) continue; const nodes: ServiceNode[] = await resp.json(); const evalCtx: EvalContext = {}; const ctx: ServiceContext = { evalCtx, lang: 'cu', t: this.t, fetchBibleText: async () => '', fetchLives: async () => null, fetchCommandText: async () => '', resolveTimes: async () => '', fetchText: async () => null, fetchPrayerNodes: async () => null, fetchServiceNodes: async () => null, }; const { html: h } = await assembleService(nodes, ctx); if (h.trim()) { html += `
${h}
`; } } catch { /* skip section */ } } const fontClass = this.getFontClass(); contentEl.className = `prose prose-sm max-w-none liturgical-text ${fontClass}`; contentEl.innerHTML = html.trim() ? html : `

${this.t.prayer.empty}

`; } }