Files
Pier_v1/Code/교각모델링앱/web/templates/review.html
T
ahnyongjooandClaude Sonnet 5.5 964a841de1 chore(rebar): Pier_T_rebar 작업 폴더 초기 구성 및 외부 의존 파일 통합
- Code\rebar_pipeline, review_pipeline, pier-program을 정션에서 실제 폴더로 전환
- Code\rhino_mcp 및 CLAUDE_rhino배경.md 추가, .mcp.json은 PYTHONPATH로 새 위치 사용
- pier-program 내부 .git 제거(이력은 backup\pier-program_20261028.bundle에 보관)
- worktree 가이드 반영(CLAUDE.local.md, HANDOFF.md)
- D:\Rhino-MCP 경로 참조를 새 위치로 치환

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 11:20:18 +09:00

109 lines
8.9 KiB
HTML

{% extends "base.html" %}
{% from "_prism.html" import st_badge %}
{% block title %}검토 요약 확정 · {{ p.name }}{% endblock %}
{% block body %}
<div class="prism">
<div class="stack">
<div class="stack" style="gap:var(--prism-space-100)">
<span class="eyebrow">입력 묶음 · 검토 요약 확정</span>
<h1 style="font-size:var(--prism-type-title);color:var(--prism-text-heading);margin:0">{{ p.name }} — 한 화면에서 검토하고 한 번에 확정</h1>
</div>
{% if not items %}
<section class="strip"><p class="concl">확정이 필요한 묶음이 없습니다.</p>
<p class="cap">필요한 묶음이 모두 확정되어 있습니다. 값을 고치면 해당 묶음만 다시 여기에 나타납니다.</p></section>
<div class="row"><a class="btn" href="{{ url_for('pier', name=p.name) }}">교각 화면으로</a></div>
{% else %}
<section class="strip">
<p class="concl">확정이 필요한 묶음 {{ items|length }}개 — 일치 {{ cnt.A }} · 주의·근거 없음 {{ cnt.B }} · 결정 기록 {{ cnt.D }} · 불일치 {{ cnt.C }}</p>
<p class="cap">앱이 <b>무엇이 바뀌었는지</b>, <b>앱이 무엇을 확인했는지</b>, <b>앱이 확인하지 못한 것</b>을 정리했습니다. 확정은 사람의 기록이므로 <b>도면과 대조한 묶음에 직접 체크</b>해야 하고,
체크하지 않은 묶음은 미확정으로 남습니다. 사유는 앱이 아는 사실(앱 확인·변경·출처)과 아래 의견으로 자동 기록됩니다.</p>
</section>
<form method="post" id="rf" class="stack">
{% if by_group.C %}
<h2>불일치 {{ cnt.C }}개 — 확정할 수 없습니다</h2>
<div class="banner dont" role="alert"><span class="m" aria-hidden="true">■</span><div>값을 고치거나 도면과 다른 이유를 해결한 뒤 다시 확인하세요(묶음을 열어 고치면 이 화면에 다시 나타납니다).</div></div>
<div class="tw"><table class="t"><thead><tr><th scope="col">묶음</th><th scope="col">앱 확인</th><th scope="col">불일치 내용</th></tr></thead><tbody>
{% for i in by_group.C %}<tr><td><a href="{{ url_for('evidence', name=p.name, which=i.file, key=i.key) }}">{{ i.id }}</a></td><td>{{ st_badge(i.st) }}</td><td>{{ i.summary }}</td></tr>{% endfor %}
</tbody></table></div>
{% endif %}
{% if by_group.B %}
<h2>주의·근거 없음 {{ cnt.B }}개 — 도면과 대조한 것에 체크</h2>
<div class="row"><button class="btn" type="button" id="all-on" data-flagged="{{ by_group.B|selectattr('flags')|list|length + by_group.D|selectattr('flags')|list|length }}">주의·근거 없음·결정 기록 모두 도면과 대조함으로 체크</button>
<button class="btn quiet" type="button" id="all-off">모두 해제</button></div>
<p class="cap">앱이 독립적으로 대조하지 못했거나 일부만 확인한 묶음입니다. 아래 "앱이 확인하지 못한 것"을 도면과 직접 대조한 뒤 체크하세요. 체크 하나가 그 묶음의 확정 기록입니다.</p>
<div class="tw"><table class="t"><thead><tr><th scope="col">대조함</th><th scope="col">묶음</th><th scope="col">바뀐 것</th><th scope="col">앱이 확인하지 못한 것</th><th scope="col">값의 출처</th></tr></thead><tbody>
{% for i in by_group.B %}
<tr><td><input type="checkbox" name="ack" value="{{ i.id }}" aria-label="{{ i.id }} 도면과 대조함" style="width:1.4rem;height:1.4rem"></td>
<td><b><a href="{{ url_for('evidence', name=p.name, which=i.file, key=i.key) }}">{{ i.id }}</a></b><br>{{ st_badge(i.st) }}{% if i.stale %} <span class="cap muted">변경됨(이전 확정 {{ i.prev[0] }})</span>{% endif %}</td>
<td>{{ i.change.text }}{% for fl in i.flags %}<br><span class="b warn"><span class="m" aria-hidden="true">▲</span>{{ fl }}</span>{% endfor %}</td>
<td>{% if i.unverified %}<ul style="margin:0;padding-left:1.1rem">{% for u in i.unverified[:3] %}<li>{{ u }}</li>{% endfor %}</ul>{% else %}{{ i.summary }}{% endif %}</td>
<td>{% if i.prov %}{% for pv in i.prov %}<span class="chip">{{ pv }}</span> {% endfor %}{% else %}<span class="cap muted">기록 없음</span>{% endif %}</td></tr>
{% endfor %}
</tbody></table></div>
{% endif %}
{% if by_group.D %}
<h2>사람 결정 기록 {{ cnt.D }}개 — 내용을 읽고 체크</h2>
<div class="tw"><table class="t"><thead><tr><th scope="col">읽음</th><th scope="col">묶음</th><th scope="col">내용</th></tr></thead><tbody>
{% for i in by_group.D %}
<tr><td><input type="checkbox" name="ack" value="{{ i.id }}" aria-label="{{ i.id }} 읽었음" style="width:1.4rem;height:1.4rem"></td>
<td><b>{{ i.id }}</b><br>{{ st_badge(i.st) }}</td>
<td>{% if i.detail %}<ul style="margin:0;padding-left:1.1rem">{% for d in i.detail %}<li>{{ d }}</li>{% endfor %}</ul>
<p class="cap muted">계획된 FAIL(승인된 예외)은 도면과 재료표의 알려진 차이를 받아들이는 사람의 결정입니다. 설계자 확인 전 임시 등록은 사유에 그렇게 적혀 있어야 합니다.</p>
{% else %}{{ i.change.text }}{% endif %}{% for fl in i.flags %}<br><span class="b warn"><span class="m" aria-hidden="true">▲</span>{{ fl }}</span>{% endfor %}</td></tr>
{% endfor %}
</tbody></table></div>
{% endif %}
{% if by_group.A %}
<h2>앱 확인 일치 {{ cnt.A }}개 — 근거를 훑어보세요</h2>
<div class="tw"><table class="t"><thead><tr><th scope="col">묶음</th><th scope="col">바뀐 것</th><th scope="col">앱이 확인한 것</th></tr></thead><tbody>
{% for i in by_group.A %}
{% set c = (i.checks | selectattr('st', 'equalto', 'g') | selectattr('strength', 'in', ['S', 'F', 'C']) | list) %}
<tr><td><b><a href="{{ url_for('evidence', name=p.name, which=i.file, key=i.key) }}">{{ i.id }}</a></b></td><td>{{ i.change.text }}</td>
<td>{% if c %}{{ c[0].id }} {{ c[0].text }}{% if c[0].pos %} <span class="chip">{{ c[0].pos }}</span>{% endif %}{% else %}{{ i.summary }}{% endif %}</td></tr>
{% endfor %}
</tbody></table></div>
<label class="row" style="gap:.5rem"><input type="checkbox" name="ack_matched" value="1" style="width:1.4rem;height:1.4rem"> 일치 {{ cnt.A }}개의 근거를 읽었습니다(앱이 독립 근거와 같다고 확인한 묶음입니다)</label>
{% endif %}
<h2>확정</h2>
<div class="field"><label for="rby">확정자<span class="req">필수</span></label>
<input id="rby" type="text" name="by" required style="max-width:20rem" disabled>
<span class="help" id="rhint">목록을 끝까지 내리면 입력할 수 있습니다.</span></div>
<div class="field"><label for="rnote">공통 의견 (선택)</label>
<input id="rnote" type="text" name="note" style="max-width:40rem" placeholder="예: 운곡교 P5 도면(2026-10 개정)과 대조" disabled>
<span class="help">모든 확정 기록의 사유 끝에 붙습니다. 앱이 쓰는 사유(앱 확인·변경·출처)는 따로 자동으로 남습니다.</span></div>
<div class="row end"><a class="btn" href="{{ url_for('pier', name=p.name) }}">취소</a>
<button class="btn primary" id="rok" type="submit" disabled>선택한 묶음 확정</button></div>
<div id="rend" aria-hidden="true" style="height:1px"></div>
</form>
<script>
(function () {
var on = document.getElementById('all-on'), off = document.getElementById('all-off');
function boxes() { return document.querySelectorAll('input[name=ack]'); }
if (on) on.addEventListener('click', function () {
var n = parseInt(on.getAttribute('data-flagged') || '0', 10);
if (n && !confirm('주의 표시가 붙은 묶음 ' + n + '개(템플릿 원본 그대로이거나 설계자 확인 전 임시 등록)가 포함됩니다. 모두 도면과 대조했습니까?')) return;
boxes().forEach(function (b) { b.checked = true; });
});
if (off) off.addEventListener('click', function () { boxes().forEach(function (b) { b.checked = false; }); });
})();
(function () {
var end = document.getElementById('rend'), by = document.getElementById('rby'), note = document.getElementById('rnote'), ok = document.getElementById('rok'), hint = document.getElementById('rhint'), sent = false;
function done() {
if (sent) return; sent = true;
fetch({{ url_for('review_read', name=p.name)|tojson }}, {method: 'POST'}).then(function () {
by.disabled = false; note.disabled = false; ok.disabled = false; hint.textContent = '목록을 끝까지 읽었습니다. 이름을 입력하고 확정하세요.'; by.focus();
});
}
if ('IntersectionObserver' in window) { new IntersectionObserver(function (e) { if (e[0].isIntersecting) done(); }).observe(end); } else { done(); }
})();
</script>
{% endif %}
</div>
</div>
{% endblock %}