@@ -3605,6 +3605,51 @@ def render_user_dashboard_html(
36053605 if (cats.length === 0) {
36063606 categoryEl.innerHTML = '<div class=\" empty\" style=\" color:#111827;padding:12px\" >' + tt('dash.dyn.pdca.no_check_data','점검 데이터가 없습니다.') + '</div>';
36073607 } else {
3608+ // 통제 카탈로그 트리와 동일하게 프레임워크 → 도메인 → 섹션(카테고리) 계층으로 묶어 매칭.
3609+ const _fwLabel = { 'isms-p': 'ISMS-P', 'iso27001': 'ISO 27001:2022', 'custom': 'Custom / 법령' };
3610+ // cats 는 payload 에서 이미 트리 순서(ISMS-P 먼저 · 섹션번호 자연정렬)로 정렬됨.
3611+ const groups = []; // {fw, domain, rows:[], agg:{}}
3612+ cats.forEach(c => {
3613+ const fw = c.framework || '', dom = c.domain || c.category;
3614+ let g = groups.length ? groups[groups.length-1] : null;
3615+ if (!g || g.fw !== fw || g.domain !== dom) {
3616+ g = { fw, domain: dom, rows: [], agg: {pass:0,fail:0,warning:0,not_checked:0,total:0} };
3617+ groups.push(g);
3618+ }
3619+ g.rows.push(c);
3620+ g.agg.pass += c.pass; g.agg.fail += c.fail; g.agg.warning += c.warning;
3621+ g.agg.not_checked += c.not_checked; g.agg.total += c.total;
3622+ });
3623+ let lastFw = null;
3624+ const body = groups.map(g => {
3625+ let out = '';
3626+ if (g.fw !== lastFw) {
3627+ lastFw = g.fw;
3628+ out += `<tr style=\" background:#f9fafb\" ><td colspan=\" 6\" style=\" padding:6px 8px;color:#2563eb;font-weight:700;font-size:12px\" >${escapeHtml(_fwLabel[g.fw]||g.fw||'')}</td></tr>`;
3629+ }
3630+ const domSameAsSec = (g.rows.length === 1 && g.rows[0].category === g.domain);
3631+ if (!domSameAsSec) {
3632+ out += `<tr style=\" border-bottom:1px solid #e5e7eb\" >
3633+ <td style=\" padding:6px 8px;color:#111827;font-weight:700\" >${escapeHtml(g.domain)}</td>
3634+ <td style=\" text-align:right;padding:6px 8px;color:#16a34a;font-weight:700\" >${g.agg.pass}</td>
3635+ <td style=\" text-align:right;padding:6px 8px;color:#dc2626;font-weight:700\" >${g.agg.fail}</td>
3636+ <td style=\" text-align:right;padding:6px 8px;color:#ea580c;font-weight:700\" >${g.agg.warning}</td>
3637+ <td style=\" text-align:right;padding:6px 8px;color:#111827;font-weight:700\" >${g.agg.not_checked}</td>
3638+ <td style=\" text-align:right;padding:6px 8px;color:#111827;font-weight:700\" >${g.agg.total}</td></tr>`;
3639+ }
3640+ out += g.rows.map(c => {
3641+ const pad = domSameAsSec ? '6px 8px' : '5px 8px 5px 22px';
3642+ const fw2 = domSameAsSec ? 'font-weight:700' : 'font-weight:400';
3643+ return `<tr style=\" border-bottom:1px solid #f1f5f9\" >
3644+ <td style=\" padding:${pad};color:#111827;${fw2}\" >${escapeHtml(c.category)}</td>
3645+ <td style=\" text-align:right;padding:${pad};color:#16a34a\" >${c.pass}</td>
3646+ <td style=\" text-align:right;padding:${pad};color:#dc2626\" >${c.fail}</td>
3647+ <td style=\" text-align:right;padding:${pad};color:#ea580c\" >${c.warning}</td>
3648+ <td style=\" text-align:right;padding:${pad};color:#111827\" >${c.not_checked}</td>
3649+ <td style=\" text-align:right;padding:${pad};color:#111827\" >${c.total}</td></tr>`;
3650+ }).join('');
3651+ return out;
3652+ }).join('');
36083653 categoryEl.innerHTML = `<table style=\" width:100%;border-collapse:collapse;font-size:13px\" >
36093654 <thead><tr style=\" color:#111827;border-bottom:1px solid #e5e7eb\" >
36103655 <th style=\" text-align:left;padding:6px 8px\" >${tt('dash.dyn.pdca.category','카테고리')}</th>
@@ -3613,16 +3658,7 @@ def render_user_dashboard_html(
36133658 <th style=\" text-align:right;padding:6px 8px\" >Warning</th>
36143659 <th style=\" text-align:right;padding:6px 8px\" >${tt('dash.dyn.pdca.not_checked','미점검')}</th>
36153660 <th style=\" text-align:right;padding:6px 8px\" >${tt('dash.dyn.lbl.total','합계')}</th>
3616- </tr></thead><tbody>`
3617- + cats.map(c => `<tr style=\" border-bottom:1px solid #e5e7eb\" >
3618- <td style=\" padding:6px 8px;color:#111827;font-weight:600\" >${escapeHtml(c.category)}</td>
3619- <td style=\" text-align:right;padding:6px 8px;color:#16a34a\" >${c.pass}</td>
3620- <td style=\" text-align:right;padding:6px 8px;color:#dc2626\" >${c.fail}</td>
3621- <td style=\" text-align:right;padding:6px 8px;color:#ea580c\" >${c.warning}</td>
3622- <td style=\" text-align:right;padding:6px 8px;color:#111827\" >${c.not_checked}</td>
3623- <td style=\" text-align:right;padding:6px 8px;color:#111827\" >${c.total}</td>
3624- </tr>`).join('')
3625- + '</tbody></table>';
3661+ </tr></thead><tbody>` + body + '</tbody></table>';
36263662 }
36273663 }
36283664 // Pending remediations (control_check + trivy + alert)
0 commit comments