// Shared helpers for the ISO/IEC 29110 work-product seed. // Every document follows the layout of the audited 200-TAS-25-001-00 package: // title, document-control block, Thai section headings, tables, three signature tables. export const TH_MONTHS = ['มกราคม','กุมภาพันธ์','มีนาคม','เมษายน','พฤษภาคม','มิถุนายน','กรกฎาคม','สิงหาคม','กันยายน','ตุลาคม','พฤศจิกายน','ธันวาคม']; export const TH_SHORT = ['ม.ค.','ก.พ.','มี.ค.','เม.ย.','พ.ค.','มิ.ย.','ก.ค.','ส.ค.','ก.ย.','ต.ค.','พ.ย.','ธ.ค.']; const parts = (iso) => iso.split('-').map(Number); /** 2026-08-17 -> 25690817 (Buddhist compact, used in filenames and Release fields). */ export const bud = (iso) => { const [y, m, d] = parts(iso); return `${y + 543}${String(m).padStart(2, '0')}${String(d).padStart(2, '0')}`; }; /** 2026-08-17 -> 17 สิงหาคม 2569 */ export const thLong = (iso) => { const [y, m, d] = parts(iso); return `${d} ${TH_MONTHS[m - 1]} ${y + 543}`; }; /** 2026-08-17 -> 17 ส.ค. 69 */ export const thShort = (iso) => { const [y, m, d] = parts(iso); return `${d} ${TH_SHORT[m - 1]} ${String(y + 543).slice(2)}`; }; /** 2026-08-17 -> 17/08/2569 */ export const thNum = (iso) => { const [y, m, d] = parts(iso); return `${String(d).padStart(2, '0')}/${String(m).padStart(2, '0')}/${y + 543}`; }; /** Range in short Thai form: 10–14 ส.ค. 69 or 30 พ.ค.–31 ก.ค. 69 */ export const thRange = (a, b) => { if (a === b) return thShort(a); const [ya, ma, da] = parts(a); const [yb, mb, db] = parts(b); if (ya === yb && ma === mb) return `${da}–${db} ${TH_SHORT[ma - 1]} ${String(ya + 543).slice(2)}`; if (ya === yb) return `${da} ${TH_SHORT[ma - 1]}–${db} ${TH_SHORT[mb - 1]} ${String(ya + 543).slice(2)}`; return `${thShort(a)}–${thShort(b)}`; }; export const thRangeLong = (a, b) => `${thLong(a)} – ${thLong(b)}`; export const daysBetween = (a, b) => Math.round((Date.parse(b) - Date.parse(a)) / 86400000) + 1; const cell = (v) => String(v ?? '').replace(/\|/g, '\\|').replace(/\r?\n/g, ' '); /** Markdown table. `align` is an optional array of 'l' | 'c' | 'r'. */ export function table(headers, rows, align) { const sep = headers.map((_, i) => ({ c: ':---:', r: '---:' }[align?.[i]] ?? '---')); const line = (r) => `| ${r.map(cell).join(' | ')} |`; return [line(headers), line(sep), ...rows.map(line)].join('\n'); } /** Document-control block. Each row is [label, value] or [label, value, label2, value2]. */ export function control(rows) { return rows.map((r) => `| ${r.map(cell).join(' | ')} |`).join('\n'); } /** A single signature table (ชื่อ / ตำแหน่ง / ลายเซ็น / วันที่). */ const sigTable = (people) => table(['ชื่อ', 'ตำแหน่ง', 'ลายเซ็น', 'วันที่'], people.map((p) => [p.th, p.role, '', ''])); /** The three signature blocks used by every work product. */ export function signatures(secretary, reviewer, approver) { const list = (x) => (Array.isArray(x) ? x : [x]); return [ '## ผู้จัดทำเอกสาร (Secretary)', sigTable(list(secretary)), '## ผู้ตรวจสอบเอกสาร (Reviewer)', sigTable(list(reviewer)), '## ผู้อนุมัติ (Approval)', sigTable(list(approver)), ].join('\n\n'); } export const bullets = (items) => items.map((i) => `- ${i}`).join('\n'); export const numbered = (items) => items.map((i, n) => `${n + 1}. ${i}`).join('\n'); /** Join document parts with blank lines, dropping empty parts. */ export const doc = (...parts) => parts.flat().filter((p) => p !== undefined && p !== null && p !== '').join('\n\n') + '\n';