diff --git a/index.html b/index.html index be1c69a..d6c8094 100644 --- a/index.html +++ b/index.html @@ -17,7 +17,6 @@ -
저장 전 @@ -276,6 +275,7 @@
+

철근재료표

diff --git a/scripts/verify-cast-detail.cjs b/scripts/verify-cast-detail.cjs index 100f493..f6c7c0d 100644 --- a/scripts/verify-cast-detail.cjs +++ b/scripts/verify-cast-detail.cjs @@ -104,7 +104,9 @@ let ws, send; await until(`document.getElementById('cip-pile-cageLengthMm')?.value === '12000'`); check('pile panel shares the column connection', await ev(`document.querySelector('[data-cast-part="pile"]').textContent.includes('공통 연결 방식: 연속')`)); await shot('02-pile-input'); - await click(`document.getElementById('nav-quantity')`); + // ④ is no longer a header step (header ①②③, 2026-11-06): open the 재료표 from ③. + await click(`document.getElementById('nav-rebar')`); + await click(`[...document.querySelectorAll('button')].find(b=>b.textContent==='철근재료표 보기')`); check('quantity clearly marks incomplete design', await ev(`document.getElementById('quantity-body').textContent.includes('전체 수량 미완료') && document.getElementById('quantity-body').textContent.includes('접합부')`)); await shot('03-quantity-incomplete'); await click(`document.getElementById('save-project')`); diff --git a/scripts/verify-cast-input.cjs b/scripts/verify-cast-input.cjs index c985778..c0dbd9f 100644 --- a/scripts/verify-cast-input.cjs +++ b/scripts/verify-cast-input.cjs @@ -90,7 +90,9 @@ let ws, send; await until(`document.getElementById('cip-pile-cageLengthMm')?.value === '12000'`); check('pile panel shares the column connection', await ev(`document.querySelector('[data-cast-part="pile"]').textContent.includes('공통 연결 방식: 연속')`)); await shot('02-pile-input'); - await click(`document.getElementById('nav-quantity')`); + // ④ is no longer a header step (header ①②③, 2026-11-06): open the 재료표 from ③. + await click(`document.getElementById('nav-rebar')`); + await click(`[...document.querySelectorAll('button')].find(b=>b.textContent==='철근재료표 보기')`); check('quantity clearly marks incomplete design', await ev(`document.getElementById('quantity-body').textContent.includes('전체 수량 미완료') && document.getElementById('quantity-body').textContent.includes('접합부')`)); await shot('03-quantity-incomplete'); await click(`document.getElementById('save-project')`); diff --git a/scripts/verify-cast-rebar.cjs b/scripts/verify-cast-rebar.cjs index 84ac1eb..f65691d 100644 --- a/scripts/verify-cast-rebar.cjs +++ b/scripts/verify-cast-rebar.cjs @@ -81,7 +81,9 @@ const delay=ms=>new Promise(r=>setTimeout(r,ms)); let ws,send; await send('Emulation.setDeviceMetricsOverride',{width,height:1000,deviceScaleFactor:1,mobile:false});await delay(150); check(`header ${width} keeps one row and no horizontal overflow`,await ev(`document.querySelector('.top').getBoundingClientRect().height<=65&&document.documentElement.scrollWidth<=innerWidth`)); } - await click(`document.getElementById('nav-quantity')`); + // ④ is no longer a header step (header ①②③, 2026-11-06): open the 재료표 from ③. + await click(`document.getElementById('nav-rebar')`); + await click(`[...document.querySelectorAll('button')].find(b=>b.textContent==='철근재료표 보기')`); check('quantity contains actual dedicated mains and reports remaining incomplete details',await ev(`document.getElementById('quantity-body').textContent.includes('말뚝-P1')&&document.getElementById('quantity-body').textContent.includes('기둥-P1')&&document.getElementById('quantity-body').textContent.includes('전체 수량 미완료')`)); await shot('04-quantity'); await click(`document.getElementById('nav-rebar')`);await member('기둥');const beforeSave=await ev(table); diff --git a/scripts/verify-unified-input.cjs b/scripts/verify-unified-input.cjs index 16065bd..688bb96 100644 --- a/scripts/verify-unified-input.cjs +++ b/scripts/verify-unified-input.cjs @@ -137,7 +137,9 @@ let ws, send; await click(`[...document.querySelectorAll('#rebar-issues li')].find(e=>e.textContent.includes(${JSON.stringify(text)}))?.querySelector('button')`); check(`${text}: warning opens unified editor`, await ev(`!!document.getElementById('cip-apply') && document.querySelector('#rebar-tabs button[aria-pressed="true"]').textContent.startsWith('기둥·말뚝')`)); } - await click(`document.getElementById('nav-quantity')`); + // ④ is no longer a header step (header ①②③, 2026-11-06): open the 재료표 from ③. + await click(`document.getElementById('nav-rebar')`); + await click(`[...document.querySelectorAll('button')].find(b=>b.textContent==='철근재료표 보기')`); check('quantity remains explicitly incomplete', await ev(`document.getElementById('quantity-body').textContent.includes('전체 수량 미완료')`)); check('no runtime exceptions', report.errors.length===0); report.result='PASS'; diff --git a/src/main.ts b/src/main.ts index a6a16c6..7e46029 100644 --- a/src/main.ts +++ b/src/main.ts @@ -142,7 +142,7 @@ interface Pier extends PierValues { } /** Part toggles in the order of 시안 ① "부재 표시". Each toggle controls one or more scene parts. */ -/** The four stages: ① 형상 생성, ② 형상계획, ③ 철근배근, ④ 재료표 (결과 화면). */ +/** The stages: ① 형상 생성, ② 형상계획, ③ 철근배근. 'quantity' is the 철근재료표 screen opened from ③ (시안: header ①②③, 2026-11-06). */ type Stage = 'shape' | 'plan' | 'rebar' | 'quantity'; /** 부재 표시 toggles (2026-11-03). `pier` parts follow the pier scenes; `abut` names follow the abutment groups (abut-scene.ts). */ @@ -2636,11 +2636,12 @@ function setStage(stage: Stage): void { placeViewer(stage); if (rebar3d) rebar3d.group.visible = stage === 'rebar'; // The highlight is the 'current' class (style.css); aria-current is set alongside it for assistive tech. - const nav: [string, Stage][] = [['nav-shape', 'shape'], ['nav-plan', 'plan'], ['nav-rebar', 'rebar'], ['nav-quantity', 'quantity']]; - for (const [id, key] of nav) { + // The 재료표 screen belongs to ③, so ③ stays highlighted while it is open. + const nav: [string, Stage[]][] = [['nav-shape', ['shape']], ['nav-plan', ['plan']], ['nav-rebar', ['rebar', 'quantity']]]; + for (const [id, keys] of nav) { const btn = $(id); - btn.classList.toggle('current', stage === key); - btn.setAttribute('aria-current', String(stage === key)); + btn.classList.toggle('current', keys.includes(stage)); + btn.setAttribute('aria-current', String(keys.includes(stage))); } renderAll(); } @@ -4478,11 +4479,11 @@ function renderRebar(): void { if (focused && state.rebarSelection.size) focused.scrollIntoView({ block: 'center' }); } -/** ③ keeps only a link to the 철근재료표 screen ④ (승인 2026-10-23). */ +/** ③ keeps a summary line and a link to the 철근재료표 screen (opened from ③; header ①②③, 2026-11-06). */ function renderQuantityLink(box: HTMLElement, rows: RebarRow[], incomplete: readonly string[] = []): void { const q = buildQuantity(rows, state.material, incomplete); const line = el('p', `철근재료표 · ${q.missing.length ? '계산된 부재 소계 · 전체 수량 미완료' : '합계'} ${q.kg.toFixed(1)} kg · 할증 포함 ${q.kgWithWaste.toFixed(1)} kg`, 'pnote'); - const link = el('button', '④ 재료표에서 보기', 'btn small'); + const link = el('button', '철근재료표 보기', 'btn small'); link.type = 'button'; link.addEventListener('click', () => setStage('quantity')); box.append(line, link); @@ -4536,7 +4537,6 @@ function renderQuantityScreen(): void { allButton.textContent = `전체 합계 (${generated}기)`; allButton.setAttribute('aria-pressed', String(state.quantityAll)); const targets = quantityTargets(); - setHeaderStatus('step4-status', '', targets.length ? (state.quantityAll ? `${targets.length}기` : targets[0].input.name) : '예정'); $('quantity-note').textContent = targets.length ? `${state.quantityAll ? `전체 ${targets.length}기` : targets[0].input.name} · 할증 ${state.material.waste}% (기본 0%, 가정). 삿갓 길이는 h − 100으로 가정(Q-K5). 교각 목록에서 고른 교각만 보며, 전체를 누르면 모든 교각을 보여 줍니다.` : '계산된 교각이 없습니다. 교각을 생성하세요.'; @@ -5900,7 +5900,7 @@ $('regen').addEventListener('click', regenerateChanged); $('nav-shape').addEventListener('click', () => setStage('shape')); $('nav-plan').addEventListener('click', () => setStage('plan')); $('nav-rebar').addEventListener('click', () => setStage('rebar')); -$('nav-quantity').addEventListener('click', () => setStage('quantity')); +$('quantity-back').addEventListener('click', () => setStage('rebar')); $('mat-fck').addEventListener('change', (ev) => setMaterial('fck', (ev.target as HTMLInputElement).value)); $('mat-waste').addEventListener('change', (ev) => setWaste((ev.target as HTMLInputElement).value)); for (const k of ['small16', 'main25', 'main35', 'main51'] as const) {