docs(sdlc): reformat work products to audited reference layout

Replace the PHP/Markdown seeding with a data-driven generator in
scripts/sdlc-seed and regenerate all 59 work products.
This commit is contained in:
Thanakorn
2026-09-09 08:09:54 +07:00
parent 758922f5c4
commit 087e7b3958
209 changed files with 8063 additions and 5762 deletions
+60
View File
@@ -0,0 +1,60 @@
// 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';