fix(view): alert [보기] highlights the bar it is about (시안 ③, spec §6.1)

[보기] only switched the member tab. The alert now names its bar (RB-F08 → B2),
and [보기] switches to the member, highlights that row, and scrolls to it.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Haiku 5.5 committed 2026-10-22 12:13:03 +09:00
1 parent 140d34a844
commit 488b65af31
3 files changed
+11 -1

No files matched your search

+3 -1
View File
@@ -31,6 +31,8 @@ export interface RebarWarning {
message: string; message: string;
/** Where the alert points: a member tab, or the 공통 설정 (for I02). */ /** Where the alert points: a member tab, or the 공통 설정 (for I02). */
target: RebarRow['member'] | 'common'; target: RebarRow['member'] | 'common';
/** The bar the alert is about, so that [보기] can highlight its row. */
bar?: string;
} }
/** Table metadata per bar (코핑철근.md, 확대기초철근 (1).md, 기둥철근 (1).md, 받침블럭철근.md). */ /** Table metadata per bar (코핑철근.md, 확대기초철근 (1).md, 기둥철근 (1).md, 받침블럭철근.md). */
@@ -154,7 +156,7 @@ export function rebarSummary(model: PierModel, m: Material = DEFAULT_MATERIAL):
const split = splitPieces(b.length, 25.4, m.fyS, m.fck, STANDARD_BAR); const split = splitPieces(b.length, 25.4, m.fyS, m.fck, STANDARD_BAR);
if (split) { if (split) {
rows.push({ member: 'footing', name: b.name, size: b.size, count: b.count, length: split.pieceLength, note: `${note}, 2조각 이음 (추천안, 겹침 ${Math.round(split.lap)})` }); rows.push({ member: 'footing', name: b.name, size: b.size, count: b.count, length: split.pieceLength, note: `${note}, 2조각 이음 (추천안, 겹침 ${Math.round(split.lap)})` });
warnings.push({ code: 'RB-F08', message: `${b.name} 길이 ${Math.round(b.length)} mm > 정척 ${STANDARD_BAR} mm: 2조각 이음을 추천안으로 적용 (Q-B6).`, target: 'footing' }); warnings.push({ code: 'RB-F08', message: `${b.name} 길이 ${Math.round(b.length)} mm > 정척 ${STANDARD_BAR} mm: 2조각 이음을 추천안으로 적용 (Q-B6).`, target: 'footing', bar: b.name });
} else { } else {
rows.push({ member: 'footing', name: b.name, size: b.size, count: b.count, length: b.length, note }); rows.push({ member: 'footing', name: b.name, size: b.size, count: b.count, length: b.length, note });
} }
+7
View File
@@ -74,6 +74,8 @@ const state = {
stage: 'shape' as 'shape' | 'plan' | 'rebar', stage: 'shape' as 'shape' | 'plan' | 'rebar',
/** ③ 공통 설정 (fck · fy). */ /** ③ 공통 설정 (fck · fy). */
material: { ...DEFAULT_MATERIAL } as Material, material: { ...DEFAULT_MATERIAL } as Material,
/** Bar highlighted on ③ after [보기] (시안 ③ 알림). */
rebarFocus: null as string | null,
/** Member tab on ③ (철근표). */ /** Member tab on ③ (철근표). */
rebarMember: 'coping' as RebarRow['member'], rebarMember: 'coping' as RebarRow['member'],
/** Member card selected on ② (시안 ② 부재 목록). */ /** Member card selected on ② (시안 ② 부재 목록). */
@@ -737,6 +739,7 @@ function renderRebar(): void {
nameCell.classList.add('bar-no'); nameCell.classList.add('bar-no');
// Clicking a 번호 highlights that row (시안: 번호를 누르면 그 철근만 강조). // Clicking a 번호 highlights that row (시안: 번호를 누르면 그 철근만 강조).
nameCell.addEventListener('click', () => tr.classList.toggle('hl')); nameCell.addEventListener('click', () => tr.classList.toggle('hl'));
if (r.name === state.rebarFocus) tr.classList.add('hl');
tr.append( tr.append(
el('td', ROLE[r.name] ?? '—'), el('td', ROLE[r.name] ?? '—'),
nameCell, nameCell,
@@ -766,7 +769,9 @@ function renderRebar(): void {
$<HTMLInputElement>('mat-fck').focus(); $<HTMLInputElement>('mat-fck').focus();
$<HTMLInputElement>('mat-fck').scrollIntoView({ block: 'center' }); $<HTMLInputElement>('mat-fck').scrollIntoView({ block: 'center' });
} else { } else {
// [보기]: go to the member and highlight the bar the alert is about.
state.rebarMember = w.target; state.rebarMember = w.target;
state.rebarFocus = w.bar ?? null;
renderAll(); renderAll();
} }
}); });
@@ -783,6 +788,8 @@ function renderRebar(): void {
} }
$('step3-status').textContent = `계산됨 · 경고 ${warnings.length}`; $('step3-status').textContent = `계산됨 · 경고 ${warnings.length}`;
renderMaterial(); renderMaterial();
const focused = box.querySelector('tr.hl');
if (focused && state.rebarFocus) focused.scrollIntoView({ block: 'center' });
} }
/** 구분 (role) of each bar, as in the 시안 ③ table. */ /** 구분 (role) of each bar, as in the 시안 ③ table. */
+1
View File
@@ -46,6 +46,7 @@ describe('③ 철근표 요약 (P1)', () => {
it('B9: 320 bars; warnings: B2 needs a splice (13646 > 12000)', () => { it('B9: 320 bars; warnings: B2 needs a splice (13646 > 12000)', () => {
expect(count('B9')).toBe(320); expect(count('B9')).toBe(320);
expect(warnings.map((w) => w.code)).toEqual(['I02', 'RB-F08']); // defaults are assumed values (I02) expect(warnings.map((w) => w.code)).toEqual(['I02', 'RB-F08']); // defaults are assumed values (I02)
expect(warnings.find((w) => w.code === 'RB-F08')!.bar).toBe('B2');
// B2 is split into two pieces (추천안): each piece is about half the 13646 bar plus half the lap. // B2 is split into two pieces (추천안): each piece is about half the 13646 bar plus half the lap.
expect(rows.find((r) => r.name === 'B2')!.length!).toBeCloseTo(7776, 0); expect(rows.find((r) => r.name === 'B2')!.length!).toBeCloseTo(7776, 0);
}); });