83 lines
4.7 KiB
TypeScript
83 lines
4.7 KiB
TypeScript
import { selectOrganizationPositionCounts } from '../data/content-selectors.js';
|
|
import type { RenderContext } from './render.js';
|
|
|
|
function text(document: Document, tag: string, className: string, value: string): HTMLElement {
|
|
const element = document.createElement(tag);
|
|
element.className = className;
|
|
element.textContent = value;
|
|
return element;
|
|
}
|
|
|
|
/** Independent, passive content component; composition controls its visibility and placement. */
|
|
export function createPositionSummary(document: Document, context: RenderContext): HTMLElement {
|
|
const section = document.createElement('section');
|
|
section.className = 'hm-orgchart-summary hm-orgchart-position-summary';
|
|
section.setAttribute('aria-label', '조직 및 직위별 원본 인원 현황');
|
|
section.dataset.contentSection = 'position-summary';
|
|
section.append(text(document, 'h3', 'hm-orgchart-summary-title', '조직 · 직위별 인원 현황'));
|
|
section.append(text(document, 'p', 'hm-orgchart-raw-total', `Raw Count · 원본 직원 전체 ${context.summary.raw.total}명`));
|
|
section.append(text(document, 'p', 'hm-orgchart-policy', '원본 직원 ID를 직접 소속 조직에서 한 번 집계합니다. 조직장 · 파견 · 휴직을 포함하며 업무 총원 정책을 뜻하지 않습니다.'));
|
|
section.append(text(document, 'p', 'hm-orgchart-policy', context.summary.aggregation.status === 'ready'
|
|
? `승인 정책 ${context.summary.aggregation.policyVersion}: 총원 ${context.summary.aggregation.counts.total}명`
|
|
: '정책 총원 미확정: 집계 정책 승인이 필요합니다.'));
|
|
|
|
const scroll = document.createElement('div');
|
|
scroll.className = 'hm-orgchart-summary-scroll';
|
|
scroll.tabIndex = 0;
|
|
scroll.setAttribute('role', 'region');
|
|
scroll.setAttribute('aria-label', '조직 및 직위별 인원 표, 가로 스크롤 가능');
|
|
const table = document.createElement('table');
|
|
table.className = 'hm-orgchart-position-table';
|
|
const caption = document.createElement('caption');
|
|
caption.textContent = '직위별 교차 인원과 소계는 직접 소속 기준입니다. 하위 포함 수는 서로 합산하지 않습니다.';
|
|
const positions = Object.keys(context.summary.raw.byPosition);
|
|
const matrix = selectOrganizationPositionCounts(context.data);
|
|
const head = document.createElement('thead');
|
|
const headerRow = document.createElement('tr');
|
|
for (const label of ['조직', ...positions, '소계 (직접)', '하위 포함']) {
|
|
const cell = document.createElement('th');
|
|
cell.scope = 'col';
|
|
cell.textContent = label;
|
|
headerRow.append(cell);
|
|
}
|
|
head.append(headerRow);
|
|
const body = document.createElement('tbody');
|
|
for (const organization of context.orderedOrganizations) {
|
|
const row = document.createElement('tr');
|
|
row.dataset.organizationId = organization.id;
|
|
const name = document.createElement('th');
|
|
name.scope = 'row';
|
|
name.textContent = organization.name;
|
|
row.append(name);
|
|
for (const position of positions) row.append(text(document, 'td', 'hm-orgchart-count-cell', String(matrix.get(organization.id)?.get(position) ?? 0)));
|
|
const counts = context.summary.raw.byOrganization[organization.id];
|
|
row.append(text(document, 'td', 'hm-orgchart-count-cell hm-orgchart-subtotal', String(counts?.direct ?? 0)));
|
|
row.append(text(document, 'td', 'hm-orgchart-count-cell hm-orgchart-inclusive', String(counts?.inclusive ?? 0)));
|
|
body.append(row);
|
|
}
|
|
const foot = document.createElement('tfoot');
|
|
const totalRow = document.createElement('tr');
|
|
const totalLabel = document.createElement('th');
|
|
totalLabel.scope = 'row';
|
|
totalLabel.textContent = '전체 합계';
|
|
totalRow.append(totalLabel);
|
|
for (const position of positions) totalRow.append(text(document, 'td', 'hm-orgchart-count-cell', String(context.summary.raw.byPosition[position] ?? 0)));
|
|
totalRow.append(text(document, 'td', 'hm-orgchart-count-cell', String(context.summary.raw.total)));
|
|
const inclusiveTotal = text(document, 'td', 'hm-orgchart-inclusive', '—');
|
|
inclusiveTotal.setAttribute('aria-label', '하위 포함 수는 중복되어 합산하지 않음');
|
|
totalRow.append(inclusiveTotal);
|
|
foot.append(totalRow);
|
|
table.append(caption, head, body, foot);
|
|
scroll.append(table);
|
|
section.append(scroll);
|
|
|
|
section.append(text(document, 'h4', 'hm-orgchart-group-title', '근무 상태별 원본 직원'));
|
|
const statuses = document.createElement('dl');
|
|
statuses.className = 'hm-orgchart-summary-group hm-orgchart-status-totals';
|
|
for (const [key, label] of [['active', '재직'], ['dispatched', '파견'], ['onLeave', '휴직']] as const) {
|
|
statuses.append(text(document, 'dt', '', label), text(document, 'dd', '', `${context.summary.raw.byStatus[key]}명`));
|
|
}
|
|
section.append(statuses);
|
|
return section;
|
|
}
|