Replace the PHP/Markdown seeding with a data-driven generator in scripts/sdlc-seed and regenerate all 59 work products.
61 lines
3.8 KiB
JavaScript
61 lines
3.8 KiB
JavaScript
// 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';
|