KB-678A

Constitution Matrix UI — index.html v1 (4+3 model)

27 min read Revision 1
laws-newconstitution-newui-previewmatrix4plus3artifacthtmlv1
<!DOCTYPE html> <html lang="vi"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Hiến pháp Ma trận đa chiều — constitution-new (4+3)</title> <style> :root{ --bg:#0c0f14; --panel:#141922; --panel2:#1b212d; --line:#2a3340; --ink:#e8edf4; --mut:#8b97a8; --dim:#5d6b80; --green:#3fb950; --yellow:#d6a020; --red:#f04f4f; --gray:#6b7787; --blue:#4d8cf0; --violet:#9a6cf0; --cyan:#36c4c0; --orange:#e07b2d; --chip:#202836; } *{box-sizing:border-box} body{margin:0;background:var(--bg);color:var(--ink); font:14px/1.55 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif} .wrap{max-width:1240px;margin:0 auto;padding:26px 20px 90px} .topbar{display:flex;flex-wrap:wrap;gap:8px;align-items:center;font-size:12px;color:var(--mut);margin-bottom:14px} .tag{background:var(--chip);border:1px solid var(--line);border-radius:6px;padding:3px 9px;color:var(--mut)} .tag.draft{color:var(--yellow);border-color:#4a3c12;background:#241d09} .tag.gcos{color:var(--cyan);border-color:#143b3a;background:#08201f} h1{font-size:25px;margin:6px 0 4px} .sub{color:var(--mut);font-size:13px;margin-bottom:8px} .sub b{color:var(--ink)} .model{display:flex;flex-wrap:wrap;gap:8px;margin:10px 0 4px;font-size:12px} .model .grp{background:var(--panel2);border:1px solid var(--line);border-radius:8px;padding:7px 11px} .model .grp b{color:var(--ink)} .model .grp.def{border-left:3px solid var(--blue)} .model .grp.ops{border-left:3px solid var(--orange)} .model .grp.out{border-left:3px solid var(--dim);color:var(--dim)} section{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:18px;margin:18px 0} h2{font-size:15px;margin:0 0 4px;display:flex;align-items:center;gap:8px} h2 .num{font-size:11px;color:var(--mut);background:var(--chip);border:1px solid var(--line);border-radius:5px;padding:1px 7px} .lead{color:var(--mut);font-size:12.5px;margin:0 0 14px;max-width:95ch} .lead b{color:var(--ink)} .selbar{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:14px} .selbar .sel{flex:1 1 180px;min-width:160px} .selbar .lbl{font-size:10px;letter-spacing:.5px;text-transform:uppercase;color:var(--dim);margin-bottom:4px} select{width:100%;background:var(--bg);border:1px solid var(--line);color:var(--ink);border-radius:8px;padding:8px 10px;font:inherit;font-size:12.5px} .selbar .sel.d1 .lbl{color:var(--blue)} .selbar .sel.d2 .lbl{color:var(--violet)} .selbar .sel.d3 .lbl{color:var(--cyan)} .selbar .sel.d4 .lbl{color:var(--green)} .reset{background:var(--chip);border:1px solid var(--line);color:var(--mut);border-radius:8px;padding:8px 12px;cursor:pointer;font:inherit;align-self:flex-end} .reset:hover{color:var(--ink)} .mscroll{overflow-x:auto;border:1px solid var(--line);border-radius:10px} table.matrix{border-collapse:collapse;width:100%;min-width:1000px;font-size:12.5px} table.matrix th,table.matrix td{border-bottom:1px solid var(--line);padding:8px 10px;text-align:left;vertical-align:middle} table.matrix thead th{background:var(--panel2);color:var(--mut);font-weight:600;font-size:10.5px;letter-spacing:.3px;text-transform:uppercase;white-space:nowrap} table.matrix thead th.gdef{color:var(--blue)} table.matrix thead th.gops{color:var(--orange)} table.matrix tbody tr{cursor:pointer} table.matrix tbody tr:hover{background:#171d27} table.matrix tbody tr.sel{background:#10212e;outline:1px solid var(--cyan)} td.loai b{color:var(--ink)} .pill{display:inline-block;font-size:11px;padding:1px 7px;border-radius:5px;border:1px solid var(--line);background:var(--chip);color:var(--mut);white-space:nowrap} .pill.k{color:var(--orange);border-color:#3d2c12;background:#21160a} .pill.dot{color:var(--violet);border-color:#2c2349;background:#160f24} .pill.miss{color:var(--red);border-color:#4a2020;background:#220e0e} .pill.ok{color:var(--green);border-color:#1f3d23;background:#10210f} .mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11.5px;color:var(--ink)} .lamp{width:10px;height:10px;border-radius:50%;display:inline-block} .lamp.g{background:var(--green)} .lamp.y{background:var(--yellow)} .lamp.r{background:var(--red)} .lamp.x{background:var(--gray)} td.tt{text-align:center} .legend{display:flex;gap:16px;flex-wrap:wrap;margin-top:12px;font-size:11.5px;color:var(--mut)} .legend span{display:inline-flex;align-items:center;gap:6px} .ocard{margin-top:14px;background:var(--panel2);border:1px solid var(--line);border-radius:10px;padding:14px} .ocard h3{margin:0 0 10px;font-size:14px} .ocard .coord{font-size:12px;color:var(--mut);margin-bottom:12px} .ocard .coord b{color:var(--ink)} .layers{display:grid;grid-template-columns:repeat(3,1fr);gap:10px} .layer{background:var(--bg);border:1px solid var(--line);border-radius:9px;padding:10px} .layer .h{font-size:10px;letter-spacing:.5px;text-transform:uppercase;color:var(--dim);margin-bottom:6px} .layer.l1{border-top:2px solid var(--orange)} .layer.l2{border-top:2px solid var(--violet)} .layer.l3{border-top:2px solid var(--green)} .layer .row{font-size:12px;margin:3px 0;color:var(--mut)} .layer .row b{color:var(--ink)} .qh{margin-top:12px;font-size:12px;color:var(--mut)} .qh .badge{display:inline-block;margin:3px 5px 0 0;padding:2px 8px;border-radius:20px;border:1px solid var(--line);background:var(--chip);font-size:11px;color:var(--cyan)} .empty{color:var(--dim);font-style:italic;font-size:12.5px} .dual{display:grid;grid-template-columns:1fr 1fr;gap:14px} .col{background:var(--panel2);border:1px solid var(--line);border-radius:10px;padding:14px} .col.ct{border-top:3px solid var(--orange)} .col.dt{border-top:3px solid var(--violet)} .col h3{margin:0 0 4px;font-size:14px} .col .desc{font-size:12px;color:var(--mut);margin:0 0 10px} .enum{display:flex;flex-direction:column;gap:6px} .enum .it{display:flex;align-items:center;gap:8px;background:var(--bg);border:1px solid var(--line);border-radius:7px;padding:7px 10px;font-size:12px} .enum .it .name{font-family:ui-monospace,Menlo,monospace;color:var(--ink)} .enum .it .ex{color:var(--mut);margin-left:auto;font-size:11px} .warnline{margin-top:10px;font-size:11.5px;color:var(--yellow);background:#241d09;border:1px solid #4a3c12;border-radius:7px;padding:8px 10px} table.gov{border-collapse:collapse;width:100%;font-size:12px;min-width:760px} table.gov th,table.gov td{border:1px solid var(--line);padding:8px 10px;text-align:left;vertical-align:top} table.gov th{background:var(--panel2);color:var(--mut);font-size:11px;text-transform:uppercase;letter-spacing:.3px} table.gov td.lv{font-weight:600;color:var(--ink);white-space:nowrap} table.gov tr.l1 td.lv{color:var(--green)} table.gov tr.l2 td.lv{color:var(--yellow)} table.gov tr.l3 td.lv{color:var(--red)} table.red{border-collapse:collapse;width:100%;font-size:12.5px} table.red th,table.red td{border-bottom:1px solid var(--line);padding:8px 10px;text-align:left} table.red th{color:var(--mut);font-size:11px;text-transform:uppercase} table.red td:first-child{color:var(--dim);width:34px} table.red code,.lead code,.foot code{color:var(--cyan);font-size:11.5px} .cols2{display:grid;grid-template-columns:1fr 1fr;gap:14px} .mini{background:var(--panel2);border:1px solid var(--line);border-radius:9px;padding:12px} .mini h4{margin:0 0 8px;font-size:12.5px;color:var(--ink)} .mini .kv{font-size:12px;color:var(--mut);margin:4px 0} .mini .kv b{color:var(--ink)} .gcosbox{display:grid;grid-template-columns:1fr 1fr;gap:8px} .gcosbox .pair{background:var(--bg);border:1px solid var(--line);border-radius:7px;padding:8px 10px;font-size:12px} .gcosbox .pair .a{color:var(--ink)} .gcosbox .pair .b{color:var(--cyan);font-family:ui-monospace,Menlo,monospace;font-size:11px} .foot{font-size:11.5px;color:var(--dim);margin-top:10px} .callout{font-size:12.5px;color:var(--ink);background:#08201f;border:1px solid #143b3a;border-left:3px solid var(--cyan);border-radius:8px;padding:11px 13px} @media(max-width:760px){.layers,.dual,.cols2,.gcosbox{grid-template-columns:1fr}} </style> </head> <body> <div class="wrap"> <div class="topbar"> <span class="tag">laws-new</span> <span class="tag">constitution-new</span> <span class="tag draft">DRAFT — chưa enact</span> <span class="tag gcos">Matrix = view/control của GCOS · One-Roof</span> </div> <h1>Hiến pháp Ma trận đa chiều</h1> <div class="sub">Mọi việc mới phải đặt vào đúng <b>ô ma trận</b> trước. Không tạo lane/module mới nếu việc đó xử lý được bằng ô + kho tạm + công thức + DOT.</div> <div class="model"> <span class="grp def"><b>4 chiều định vị:</b> Tầng × Loài × Kho × Miền</span> <span class="grp ops"><b>3 lớp vận hành:</b> Công thức · DOT · Governance</span> <span class="grp out">Quan hệ = graph riêng · Capability = thuộc tính lọc · Task = overlay</span> </div> <section> <h2><span class="num">[1]</span> Ma trận tổng thể — 4 chiều định vị chọn ra đúng 1 ô</h2> <p class="lead"><b>4 chiều định vị</b> (Tầng · Loài · Kho · Miền) lọc ra ô. Bấm một hàng để mở <b>Object Card</b>: trên ô hiện <b>3 lớp vận hành</b> (Công thức · DOT · Governance). <b>Quan hệ</b> là graph riêng — chỉ hiện badge tóm tắt. <b>Capability</b> chỉ là thuộc tính lọc, không phải chiều.</p> <div class="selbar"> <div class="sel d1"><div class="lbl">1 · Tầng</div><select id="fTang"></select></div> <div class="sel d2"><div class="lbl">2 · Loài</div><select id="fLoai"></select></div> <div class="sel d3"><div class="lbl">3 · Kho</div><select id="fKho"></select></div> <div class="sel d4"><div class="lbl">4 · Miền chuyên môn</div><select id="fMien"></select></div> <button class="reset" id="reset">↺ Xoá lọc</button> </div> <div class="mscroll"> <table class="matrix"> <thead><tr> <th class="gdef">Tầng</th><th class="gdef">Loài</th><th class="gdef">Kho</th><th class="gdef">Miền</th> <th class="gops">Công thức (kind)</th><th class="gops">DOT</th><th class="gops">Governance</th><th>TT</th> </tr></thead> <tbody id="mbody"></tbody> </table> </div> <div class="legend"> <span><i class="lamp g"></i> đủ</span><span><i class="lamp y"></i> thiếu nhẹ / draft</span> <span><i class="lamp r"></i> lỗi / thiếu</span><span><i class="lamp x"></i> chưa rõ</span> </div> <div class="ocard" id="ocard"><div class="empty">Bấm một ô trong bảng để xem Object Card đầy đủ…</div></div> </section> <section> <h2><span class="num">LỚP 1·2</span> Công thức và DOT — hai lớp TÁCH BẠCH</h2> <p class="lead"><b>Công thức</b> = cái cần làm / cách lắp. <b>DOT</b> = máy chạy/kiểm/promote/rollback công thức. <b>quy_trình</b> là một <i>loại</i> công thức (có bước) — <b>không phải</b> DOT. Trong data/model <b>không gom DOT thành <code>formula_kind</code></b>.</p> <div class="dual"> <div class="col ct"> <h3>Công thức</h3> <div class="desc"><span class="mono">formula_kind</span> — loại công thức:</div> <div class="enum"> <div class="it"><span class="name">raw_formula</span><span class="ex">công thức gốc, không bước</span></div> <div class="it"><span class="name">quy_trình</span><span class="ex">công thức CÓ bước (workflow)</span></div> <div class="it"><span class="name">khuôn</span><span class="ex">mold — đúc ra ô cùng dạng</span></div> <div class="it"><span class="name">validation_rule</span><span class="ex">luật kiểm</span></div> <div class="it"><span class="name">promotion_rule</span><span class="ex">luật promote kho tạm→chính</span></div> <div class="it"><span class="name">rollback_rule</span><span class="ex">luật hoàn tác</span></div> </div> </div> <div class="col dt"> <h3>DOT — lớp máy riêng</h3> <div class="desc"><span class="mono">dot_role</span> — vai trò máy:</div> <div class="enum"> <div class="it"><span class="name">assemble</span><span class="ex">máy lắp — sinh ô đầu ra</span></div> <div class="it"><span class="name">validate</span><span class="ex">máy kiểm — chạy validation_rule</span></div> <div class="it"><span class="name">promote</span><span class="ex">máy promote — đưa lên kho chính</span></div> <div class="it"><span class="name">rollback</span><span class="ex">máy rollback — hoàn tác</span></div> </div> <div class="warnline">DOT không phải biến thể của công thức. DOT <b>chạy</b> công thức; một công thức có thể được nhiều DOT thực thi.</div> </div> </div> </section> <section> <h2><span class="num">[2]</span> Ma trận Governance — Làm nhanh ở kho tạm, kiểm chặt khi promote</h2> <p class="lead">Governance không còn là cửa khẩu toàn hệ. Chỉ siết khi <b>promote</b> hoặc <b>đụng lõi</b>. Mục tiêu: giảm từ hàng trăm yếu tố xuống ~<b>15–20 yếu tố cục bộ</b> mỗi lần promote, không mất an toàn.</p> <div class="mscroll"> <table class="gov"> <thead><tr><th>Mức</th><th>Kho</th><th>Công thức</th><th>DOT</th><th>Owner / Kiểm / Rollback</th></tr></thead> <tbody> <tr class="l1"><td class="lv">Workspace<br>Kho tạm</td><td>chỉ ghi kho tạm</td><td>được dùng draft formula</td><td>DOT thử nghiệm trong giới hạn</td><td>owner nhẹ · kiểm nhanh · xóa workspace là đủ</td></tr> <tr class="l2"><td class="lv">Promotion<br>Vào kho chính</td><td>ghi kho chính chỉ qua promotion DOT</td><td>phải có formula version / hash</td><td>validator + promotion DOT hợp lệ</td><td>owner/scope rõ · 15–20 điều kiện cục bộ · có rollback pointer</td></tr> <tr class="l3"><td class="lv">Canonical-Kernel<br>Lõi chính thức</td><td>kho chính + kernel</td><td>công thức canonical</td><td>full governance DOT</td><td>owner canonical · full governance · rollback + audit bắt buộc</td></tr> </tbody> </table> </div> <p class="lead" style="margin-top:12px"><b>Canonical-Kernel dành cho:</b> sửa khai sinh/birth · sửa kernel · sửa owner/scope canonical · sửa công thức canonical · tạo tầng/loài/collection canonical mới · sửa production gate · sửa approval/audit/rollback rule.</p> </section> <section> <h2><span class="num">PHẦN</span> Bản đồ 6 tầng — Atom → Building</h2> <p class="lead">Mỗi tầng chứa loài mẫu, mang 3 lớp vận hành. Việc cần làm = lấp ô thiếu / sửa ô sai, không dựng lại cả tầng.</p> <div class="cols2" id="tangMap"></div> </section> <section> <h2><span class="num">VÍ DỤ</span> Phân rã "bánh xe tuyết"</h2> <p class="lead">Cần xe tuyết không có nghĩa làm lại mọi thứ — tìm đúng ô, ô nào có thì dùng, ô nào thiếu mới đi xuống tầng dưới.</p> <div class="mini"> <div class="kv">▸ Cần xe tuyết — <b>Building / Xe tuyết</b> (đích nhắm)</div> <div class="kv">→ cần bánh xe — xuống <b>Product / Bánh xe / Kho chính</b> — <span class="pill miss">CHƯA CÓ (đỏ)</span></div> <div class="kv">→ mở ô đó ở <b>Kho tạm</b> — lắp thử. Công thức cần 3 ô:</div> <div class="kv">&nbsp;&nbsp;✓ Vật liệu / Vành — <span class="pill ok">CÓ → dùng lại</span></div> <div class="kv">&nbsp;&nbsp;✓ Vật liệu / Cao su — <span class="pill ok">CÓ → dùng lại</span></div> <div class="kv">&nbsp;&nbsp;✗ Hợp chất / Hoa lốp — <span class="pill miss">THIẾU → lắp ô này</span></div> <div class="kv">→ promote Kho tạm → Kho chính qua <b>promotion check cục bộ</b></div> </div> <div class="callout" style="margin-top:12px">💡 Chỉ <b>một ô đỏ</b> (hoa lốp) cần làm mới. Hai vật liệu đã có được tái dùng. Không "làm lại cả chiếc xe", và không phải đi qua full governance.</div> </section> <section> <h2><span class="num">GRAPH</span> Quan hệ — lớp cạnh riêng, KHÔNG phải chiều</h2> <p class="lead">Quan hệ là graph nối các ô; trên ma trận chỉ hiện badge/tóm tắt. Bộ quan hệ chuẩn để mỗi object trả lời được <b>9 câu</b>.</p> <div class="cols2"> <div class="mini"> <h4>Bộ quan hệ chuẩn</h4> <div class="kv">belongs_to / contains · made_from · uses / used_by</div> <div class="kv">depends_on · stored_in · created_by_formula</div> <div class="kv">produced_by_dot · validated_by_dot · promoted_by_dot</div> <div class="kv">governed_by / governs · promoted_from · supersedes</div> </div> <div class="mini"> <h4>9 câu mỗi object phải trả lời</h4> <div class="kv">1. Tôi thuộc ai? &nbsp; 2. Ai thuộc tôi? &nbsp; 3. Tôi dùng ai?</div> <div class="kv">4. Ai dùng tôi? &nbsp; 5. Tôi được tạo từ đâu?</div> <div class="kv">6. Công thức nào tạo tôi? &nbsp; 7. DOT nào kiểm tôi?</div> <div class="kv">8. Tôi nằm ở kho nào? &nbsp; 9. Tôi governed_by ai?</div> </div> </div> </section> <section> <h2><span class="num">LỖI ĐỎ</span> Bảng vi phạm nguyên tắc ma trận</h2> <p class="lead">Quản trị = quét tìm và đóng các ô đỏ này, không xây thêm lane.</p> <table class="red"><thead><tr><th>#</th><th>Lỗi</th><th>Ý nghĩa</th></tr></thead><tbody> <tr><td>1</td><td>Loài không có tầng</td><td>Không gắn được vào Atom…Building.</td></tr> <tr><td>2</td><td>Loài ở nhiều tầng</td><td>Cùng loài xếp 2 tầng → mất tính nguyên tử.</td></tr> <tr><td>3</td><td>Loài governed không có kho</td><td>Quản trị mà không collection → không tin cậy.</td></tr> <tr><td>4</td><td>Kho không gắn tầng/loài/miền</td><td>Kho không biết chứa loài gì, miền nào.</td></tr> <tr><td>5</td><td>Công thức không có ô đầu ra</td><td>Công thức chạy ra… không ô nào.</td></tr> <tr><td>6</td><td>Công thức cần ô đầu vào chưa tồn tại</td><td>vd bánh xe cần ô hoa lốp còn thiếu.</td></tr> <tr><td>7</td><td>DOT không gắn tầng/loài/công thức</td><td>Máy lắp <b>orphan</b>, không biết lắp ô nào.</td></tr> <tr><td>8</td><td>DOT không rõ vai trò</td><td>Không phân biệt assemble/validate/promote/rollback.</td></tr> <tr><td>9</td><td>Kho chính bị ghi thẳng không qua promote DOT</td><td>Vỡ governance.</td></tr> <tr><td>10</td><td>Luật/quy trình không quy về công thức nào</td><td>Luật "tủ kính", không map <code>formula_kind</code>.</td></tr> </tbody></table> </section> <section> <h2><span class="num">GCOS</span> Matrix là view/control layer của GCOS — KHÔNG phải governance mới</h2> <p class="lead">Không thiết kế governance mới. Không tạo registry/lane/contract mới nếu hệ cũ đã có nền tương ứng.</p> <div class="gcosbox"> <div class="pair"><span class="a">Object ownership</span> → <span class="b">SB-2</span></div> <div class="pair"><span class="a">Candidate state (kho tạm)</span> → <span class="b">SB-10</span></div> <div class="pair"><span class="a">Snapshot / ruleset</span> → <span class="b">SB-12</span></div> <div class="pair"><span class="a">Object contract</span> → <span class="b">Governed Object Contract</span></div> <div class="pair"><span class="a">Issue (ô đỏ)</span> → <span class="b">system_issues</span></div> <div class="pair"><span class="a">Audit</span> → <span class="b">registry_changelog / event_outbox</span></div> </div> <div class="foot">Track <code>laws-new/constitution-new</code> · ⚠ CHƯA enact · CHƯA thay <code>knowledge/dev/laws/</code>. Mọi luật về sau phải map được vào model 4+3 này; nếu không map được thì hoặc luật sai, hoặc ma trận còn thiếu ô. <b>Kỷ luật:</b> Ma trận để định vị · Công thức để biết cách lắp · DOT để chạy · Governance để quyết kho tạm/promote/lõi · Quan hệ ở graph riêng. Không thêm chiều mới.</div> </section> </div> <script> const CELLS = [ {tang:"Atom", loai:"information_piece", kho:"Canonical", mien:"IU", fname:"smart_brick_mold", fkind:"khuôn", dot:"assemble", gov:"ok", cap:"", qh:["made_from","used_by"], st:"g"}, {tang:"Atom", loai:"field", kho:"Canonical", mien:"Data", fname:"field_decl", fkind:"raw_formula", dot:"assemble", gov:"ok", cap:"", qh:["uses"], st:"g"}, {tang:"Atom", loai:"value / token", kho:"Workspace", mien:"Data", fname:"—", fkind:"?", dot:"—", gov:"thiếu owner", cap:"", qh:[], st:"x"}, {tang:"Molecule", loai:"record", kho:"Canonical", mien:"Data", fname:"record_compose", fkind:"quy_trình", dot:"assemble", gov:"ok", cap:"", qh:["made_from"], st:"g"}, {tang:"Molecule", loai:"axis_envelope", kho:"Canonical", mien:"IU", fname:"axis3_mold", fkind:"khuôn", dot:"assemble", gov:"ok", cap:"", qh:["uses"], st:"g"}, {tang:"Molecule", loai:"tag_set", kho:"Workspace", mien:"Collection", fname:"tag_mold", fkind:"khuôn", dot:"assemble", gov:"thiếu verdict", cap:"", qh:["uses"], st:"y"}, {tang:"Compound", loai:"form", kho:"Canonical", mien:"Customer", fname:"form_builder", fkind:"quy_trình", dot:"assemble", gov:"ok", cap:"customer-facing", qh:["depends_on"], st:"g"}, {tang:"Compound", loai:"rule_set", kho:"Workspace", mien:"Law", fname:"validation_rule", fkind:"validation_rule", dot:"validate", gov:"thiếu rule", cap:"internal", qh:["uses"], st:"y"}, {tang:"Material", loai:"dataset", kho:"Workspace", mien:"Data", fname:"?", fkind:"?", dot:"—", gov:"thiếu collection", cap:"", qh:[], st:"x"}, {tang:"Product", loai:"report", kho:"Canonical", mien:"Customer", fname:"report_gen", fkind:"quy_trình", dot:"promote", gov:"ok", cap:"customer-facing", qh:["produced_by"], st:"g"}, {tang:"Building", loai:"subsystem · MOW/MOT/MODW", kho:"Canonical", mien:"Governance", fname:"workflow_subsys", fkind:"promotion_rule", dot:"promote", gov:"thiếu rollback", cap:"internal", qh:["parent_child"], st:"y"}, {tang:"Building", loai:"module", kho:"Canonical", mien:"Governance", fname:"module_prebuilt", fkind:"khuôn", dot:"assemble", gov:"ok", cap:"", qh:["uses"], st:"g"}, ]; const TANGS = ["Atom","Molecule","Compound","Material","Product","Building"]; function uniq(arr){return arr.filter(function(v,i){return arr.indexOf(v)===i;});} function fillSelect(id, vals, label){ const s=document.getElementById(id); s.innerHTML='<option value="">'+label+'</option>'+vals.map(function(v){return '<option>'+v+'</option>';}).join(''); } fillSelect('fTang', TANGS, '— mọi tầng —'); fillSelect('fLoai', uniq(CELLS.map(function(c){return c.loai;})), '— mọi loài —'); fillSelect('fKho', uniq(CELLS.map(function(c){return c.kho;})), '— mọi kho —'); fillSelect('fMien', uniq(CELLS.map(function(c){return c.mien;})), '— mọi miền —'); function fkindPill(k){ return k==='?'?'<span class="pill miss">?</span>':'<span class="pill k">'+k+'</span>'; } function dotPill(d){ return (d==='—')?'<span class="pill miss">— orphan</span>':'<span class="pill dot">'+d+'</span>'; } function govPill(g){ return g==='ok'?'<span class="pill ok">ok</span>':'<span class="pill miss">'+g+'</span>'; } let selIdx=-1; function applyFilter(){ const t=fTang.value,l=fLoai.value,k=fKho.value,m=fMien.value; const rows=CELLS.map(function(c,i){return {c:c,i:i};}).filter(function(o){ return (!t||o.c.tang===t)&&(!l||o.c.loai===l)&&(!k||o.c.kho===k)&&(!m||o.c.mien===m); }); document.getElementById('mbody').innerHTML = rows.map(function(o){ const c=o.c; return '<tr data-i="'+o.i+'"'+(o.i===selIdx?' class="sel"':'')+'>'+ '<td>'+c.tang+'</td><td class="loai"><b>'+c.loai+'</b></td><td>'+c.kho+'</td><td>'+c.mien+'</td>'+ '<td><span class="mono">'+c.fname+'</span> '+fkindPill(c.fkind)+'</td>'+ '<td>'+dotPill(c.dot)+'</td><td>'+govPill(c.gov)+'</td>'+ '<td class="tt"><i class="lamp '+c.st+'"></i></td></tr>'; }).join('') || '<tr><td colspan="8" class="empty" style="padding:14px">Không có ô khớp bộ lọc.</td></tr>'; Array.prototype.forEach.call(document.querySelectorAll('#mbody tr[data-i]'), function(tr){ tr.addEventListener('click', function(){ showCard(parseInt(tr.getAttribute('data-i'),10)); }); }); } function showCard(i){ selIdx=i; applyFilter(); const c=CELLS[i]; const qh = c.qh.length? c.qh.map(function(q){return '<span class="badge">'+q+'</span>';}).join('') : '<span class="empty">chưa có quan hệ</span>'; document.getElementById('ocard').innerHTML = '<h3>'+c.loai+'</h3>'+ '<div class="coord">Định vị: <b>'+c.tang+'</b> · <b>'+c.loai+'</b> · <b>'+c.kho+'</b> · <b>'+c.mien+'</b>'+ (c.cap?' &nbsp;|&nbsp; capability (lọc): <b>'+c.cap+'</b>':' &nbsp;|&nbsp; capability: —')+'</div>'+ '<div class="layers">'+ '<div class="layer l1"><div class="h">Lớp 1 · Công thức</div>'+ '<div class="row">tên: <b>'+c.fname+'</b></div><div class="row">formula_kind: '+fkindPill(c.fkind)+'</div></div>'+ '<div class="layer l2"><div class="h">Lớp 2 · DOT (máy)</div>'+ '<div class="row">dot_role: '+dotPill(c.dot)+'</div><div class="row">'+(c.dot==='—'?'<b style="color:var(--red)">orphan</b>':'gắn công thức ✓')+'</div></div>'+ '<div class="layer l3"><div class="h">Lớp 3 · Governance</div>'+ '<div class="row">state: '+govPill(c.gov)+'</div><div class="row">'+(c.gov==='ok'?'đủ điều kiện':'cần đóng ô đỏ')+'</div></div>'+ '</div>'+ '<div class="qh"><b>Quan hệ (graph riêng):</b> '+qh+'</div>'; } ['fTang','fLoai','fKho','fMien'].forEach(function(id){document.getElementById(id).addEventListener('change',function(){selIdx=-1;applyFilter();document.getElementById('ocard').innerHTML='<div class="empty">Bấm một ô trong bảng để xem Object Card đầy đủ…</div>';});}); document.getElementById('reset').addEventListener('click',function(){fTang.value='';fLoai.value='';fKho.value='';fMien.value='';selIdx=-1;applyFilter();document.getElementById('ocard').innerHTML='<div class="empty">Bấm một ô trong bảng để xem Object Card đầy đủ…</div>';}); document.getElementById('tangMap').innerHTML = TANGS.map(function(t){ const items=CELLS.filter(function(c){return c.tang===t;}); const li = items.length? items.map(function(c){ return '<div class="kv"><b>'+c.loai+'</b> — '+fkindPill(c.fkind)+' '+dotPill(c.dot)+' '+govPill(c.gov)+'</div>'; }).join('') : '<div class="kv empty">— loài mẫu sẽ nạp từ DB —</div>'; return '<div class="mini"><h4>'+t+'</h4>'+li+'</div>'; }).join(''); applyFilter(); </script> </body> </html>