feat(help): B02~B09 좌측 패널 [?] 설명서 모달 · 패널 안 설명 글을 파일로 옮김(54-2)
- [?] = 좌측 패널 아래 단추 묶음 맨 왼쪽(공용 한 벌 · B02 는 아래 단추 줄 · B08·B09 는 [?] 줄만) - 모달 = 왼쪽 차례 · 오른쪽 글 · 고른 구조물 종류 · 연 상자 항목 먼저 - 글 = resources/help/<B0N>/*.md · 서버가 열 때마다 읽음(파일만 고치면 반영) · 설명서는 뼈대 - 옮김 = B03 안내 셋 · B05 구조물 저장 규칙 · 측구 주인 · 기슭막이 근거 · 칸 기본값 근거 · B06 표준 횡단면 두 줄 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NPJGtfXo6GMbcpiboQC4uK
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
# 프로젝트 등록 설명서
|
||||
|
||||
## 이 화면에서 하는 일
|
||||
|
||||
## 작업 순서
|
||||
|
||||
## 단추
|
||||
|
||||
## 주의할 점
|
||||
@@ -0,0 +1,9 @@
|
||||
# 파일입력 설명서
|
||||
|
||||
## 이 화면에서 하는 일
|
||||
|
||||
## 작업 순서
|
||||
|
||||
## 단추
|
||||
|
||||
## 주의할 점
|
||||
@@ -0,0 +1,5 @@
|
||||
# 필요한 파일
|
||||
|
||||
- 계획노선 — shapefile 한 벌(.shp·.shx·.dbf·.cpg·.prj) 5개.
|
||||
- 지형 — 포인트클라우드 LAS/LAZ 1개와 좌표계 PRJ, 래스터 기준 TFW.
|
||||
- 지형 TIF(DEM)는 선택 사항 — 없어도 지표면 분석이 진행됩니다.
|
||||
@@ -0,0 +1,5 @@
|
||||
# 고르는 방법
|
||||
|
||||
- 여러 개를 한 번에 끌어 놓으면 확장자로 카드에 자동 배정됩니다.
|
||||
- 카드의 [선택]으로 한 칸씩 지정하거나 ✕로 되돌릴 수 있습니다.
|
||||
- [임시 보관함]은 대시보드에 미리 올려 둔 자료를 가져옵니다.
|
||||
@@ -0,0 +1,5 @@
|
||||
# 알아 둘 것
|
||||
|
||||
- 이미 완료된 프로젝트에 다시 올리면 기존 분석 결과가 교체됩니다.
|
||||
- 작업 좌표계는 라이다 PRJ가 기준 — 노선 좌표계와 다르면 업로드가 막힙니다.
|
||||
- 라이다가 없으면 [LAS 없는 설계]를 켜고 도엽 등고선으로 진행합니다.
|
||||
@@ -0,0 +1,9 @@
|
||||
# 전처리 설명서
|
||||
|
||||
## 이 화면에서 하는 일
|
||||
|
||||
## 작업 순서
|
||||
|
||||
## 단추
|
||||
|
||||
## 주의할 점
|
||||
@@ -0,0 +1,9 @@
|
||||
# 종단설계 설명서
|
||||
|
||||
## 이 화면에서 하는 일
|
||||
|
||||
## 작업 순서
|
||||
|
||||
## 단추
|
||||
|
||||
## 주의할 점
|
||||
@@ -0,0 +1,5 @@
|
||||
# 구조물 · BOX암거
|
||||
|
||||
## 저장
|
||||
|
||||
- [저장]은 이 폼에 있는 칸만 바꿈 — 폼에 없는 칸(집계표·구조물도로 적은 값)은 그대로 둠 · 시설 종류를 바꾸면 새로 씀
|
||||
@@ -0,0 +1,13 @@
|
||||
# 구조물 · 기슭막이
|
||||
|
||||
## 저장
|
||||
|
||||
- [저장]은 이 폼에 있는 칸만 바꿈 — 폼에 없는 칸(집계표·구조물도로 적은 값)은 그대로 둠 · 시설 종류를 바꾸면 새로 씀
|
||||
|
||||
## 치수 근거
|
||||
|
||||
- 높이 — 계획홍수위 + 0.5~0.7m(사방기술교본 2-나:141 · 3-가:181) · 설계자 입력 · 비우면 미확정(금액에 안 들어감)
|
||||
- 계획비탈 1:0.3~0.5(사방기술교본 2-나:129 · 3-가:151) — 제안값은 품셈 표준경사
|
||||
- 둑마루 두께 0.3~0.5m(사방기술교본 2-나:143 · 3-가:201 · 3-가:219(콘크리트기슭막이) · 2-나:135(콘크리트 0.3m)) — 콘크리트 기준 · 돌쌓기는 뒷길이 + 0.30 식
|
||||
- 뒷채움 조약돌 두께 — 사방기술교본 2-나:133(「사방품셈 규정에 의하여」) → 품셈 13-4-4 [주]⑨ 원문 L7164~7171
|
||||
- ⚠ 사방(계류) 교본 기준 — 임도 배수관 유입·유출부 적용은 확인 남음
|
||||
@@ -0,0 +1,6 @@
|
||||
# 구조물 · 돌쌓기(메)
|
||||
|
||||
## 칸 기본값의 뜻
|
||||
|
||||
- **설치 측** — 「자동(성토 쪽)」 — 칸이 생기기 전의 동작을 이어받는 표식
|
||||
- **버림 콘크리트**(횡단설계) — 사용자 확정 ⑭ 「기본은 넣음」 — 두께는 KDS 44 90 00 의 100㎜
|
||||
@@ -0,0 +1,6 @@
|
||||
# 구조물 · 돌쌓기(찰)
|
||||
|
||||
## 칸 기본값의 뜻
|
||||
|
||||
- **설치 측** — 「자동(성토 쪽)」 — 칸이 생기기 전의 동작을 이어받는 표식
|
||||
- **버림 콘크리트**(횡단설계) — 사용자 확정 ⑭ 「기본은 넣음」 — 두께는 KDS 44 90 00 의 100㎜
|
||||
@@ -0,0 +1,5 @@
|
||||
# 구조물 · 물넘이포장
|
||||
|
||||
## 저장
|
||||
|
||||
- [저장]은 이 폼에 있는 칸만 바꿈 — 폼에 없는 칸(집계표·구조물도로 적은 값)은 그대로 둠 · 시설 종류를 바꾸면 새로 씀
|
||||
@@ -0,0 +1,5 @@
|
||||
# 구조물 · 배관
|
||||
|
||||
## 저장
|
||||
|
||||
- [저장]은 이 폼에 있는 칸만 바꿈 — 폼에 없는 칸(집계표·구조물도로 적은 값)은 그대로 둠 · 시설 종류를 바꾸면 새로 씀
|
||||
@@ -0,0 +1,5 @@
|
||||
# 구조물 · 비탈면 녹화
|
||||
|
||||
## 칸 기본값의 뜻
|
||||
|
||||
- **공법**(횡단설계) — 「초류종자 살포」는 여기 없음 — 토공집계에서 사면 전체로 섬(품셈 5-24 · 두 곳에 두면 이중계상 · 2026-09-15 브레인)
|
||||
@@ -0,0 +1,5 @@
|
||||
# 구조물 · 세월교
|
||||
|
||||
## 저장
|
||||
|
||||
- [저장]은 이 폼에 있는 칸만 바꿈 — 폼에 없는 칸(집계표·구조물도로 적은 값)은 그대로 둠 · 시설 종류를 바꾸면 새로 씀
|
||||
@@ -0,0 +1,6 @@
|
||||
# 구조물 · 옹벽
|
||||
|
||||
## 칸 기본값의 뜻
|
||||
|
||||
- **높이** — 기본값 · 소광리 도면 H=2.0 · 바꿀 수 있음
|
||||
- **설치 측** — 「자동(성토 쪽)」 — 칸이 생기기 전의 동작을 이어받는 표식
|
||||
@@ -0,0 +1,3 @@
|
||||
# 구조물 · 측구(옆도랑)
|
||||
|
||||
- 횡단 설계에서 관리 — 여기서 넣어도 제원·수량은 그쪽 값을 씁니다.
|
||||
@@ -0,0 +1,6 @@
|
||||
# 구조물 · 큰돌쌓기
|
||||
|
||||
## 칸 기본값의 뜻
|
||||
|
||||
- **설치 측** — 「자동(성토 쪽)」 — 칸이 생기기 전의 동작을 이어받는 표식
|
||||
- **버림 콘크리트**(횡단설계) — 사용자 확정 ⑭ 「기본은 넣음」 — 두께는 KDS 44 90 00 의 100㎜
|
||||
@@ -0,0 +1,9 @@
|
||||
# 구조물 · 흙막이
|
||||
|
||||
## 칸 기본값의 뜻
|
||||
|
||||
- **설치 측** — 「자동(성토 쪽)」 — 칸이 생기기 전의 동작을 이어받는 표식
|
||||
- **단 수(다단)** — 1 = 다단 없음 — 도메인 수치가 아니라 「단이 하나」라는 표식(기슭막이와 같은 벌)
|
||||
- **기준 올림(사면 위로)** — 0 = 자동 자리 그대로 — 올리지 않음을 뜻함(기슭막이와 같은 벌)
|
||||
- **기준 좌우 이동** — 0 = 자동 자리 그대로 — 옮기지 않음을 뜻함(기슭막이와 같은 벌)
|
||||
- **조달**(횡단설계) — 「채집」 — 별표2 「야면석 등은 가급적 현장에서 채취·사용」 권고. 구조물마다 바꿀 수 있음
|
||||
@@ -0,0 +1,9 @@
|
||||
# 횡단설계 설명서
|
||||
|
||||
## 이 화면에서 하는 일
|
||||
|
||||
## 작업 순서
|
||||
|
||||
## 단추
|
||||
|
||||
## 주의할 점
|
||||
@@ -0,0 +1,9 @@
|
||||
# 표준 횡단면 설정
|
||||
|
||||
## 변수 위치 안내
|
||||
|
||||
- 표준 편절편성 단면 모식도 — 각 설정값의 위치를 나타냅니다(실제 비율 아님).
|
||||
|
||||
## 표준횡단면 상세값
|
||||
|
||||
- L형 측구는 각 횡단면도에서 선택합니다.
|
||||
@@ -0,0 +1,9 @@
|
||||
# 상세설계 설명서
|
||||
|
||||
## 이 화면에서 하는 일
|
||||
|
||||
## 작업 순서
|
||||
|
||||
## 단추
|
||||
|
||||
## 주의할 점
|
||||
@@ -0,0 +1,9 @@
|
||||
# 수량산출 설명서
|
||||
|
||||
## 이 화면에서 하는 일
|
||||
|
||||
## 작업 순서
|
||||
|
||||
## 단추
|
||||
|
||||
## 주의할 점
|
||||
@@ -0,0 +1,9 @@
|
||||
# 원가계산 설명서
|
||||
|
||||
## 이 화면에서 하는 일
|
||||
|
||||
## 작업 순서
|
||||
|
||||
## 단추
|
||||
|
||||
## 주의할 점
|
||||
@@ -0,0 +1,38 @@
|
||||
"""54-2 [?] 설명서 — 서버가 resources/help/<페이지>/*.md 를 부를 때마다 읽음(고치면 바로 바뀜)."""
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from fastapi import HTTPException
|
||||
|
||||
from common_util.common_util_help_router import HELP_ROOT, read_help
|
||||
|
||||
|
||||
def test_every_page_has_manual_first():
|
||||
for n in range(2, 10):
|
||||
items = read_help(f"B0{n}")
|
||||
assert items and items[0]["key"] == "00_설명서"
|
||||
|
||||
|
||||
def test_title_order_and_live_edit():
|
||||
tmp_path = Path(tempfile.mkdtemp())
|
||||
page = tmp_path / "B05"
|
||||
page.mkdir()
|
||||
(page / "10_배관.md").write_text("# 배관\n\n본문", encoding="utf-8")
|
||||
(page / "00_설명서.md").write_text("제목 없음", encoding="utf-8")
|
||||
items = read_help("B05", tmp_path)
|
||||
assert [i["key"] for i in items] == ["00_설명서", "10_배관"]
|
||||
assert [i["title"] for i in items] == ["00_설명서", "배관"]
|
||||
(page / "10_배관.md").write_text("# 배관\n\n고친 글", encoding="utf-8")
|
||||
assert "고친 글" in read_help("B05", tmp_path)[1]["text"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("page", ["B01", "B10", "../B05", "b05"])
|
||||
def test_bad_page_refused(page):
|
||||
with pytest.raises(HTTPException):
|
||||
read_help(page)
|
||||
|
||||
|
||||
def test_root_is_repo_resources():
|
||||
assert HELP_ROOT.parts[-2:] == ("resources", "help")
|
||||
@@ -0,0 +1,114 @@
|
||||
/* 54-2 — [?] 설명서: B02~B09 좌측 패널 아래 단추 묶음 맨 왼쪽 · 모달 = 왼쪽 차례 · 오른쪽 파일 글 ·
|
||||
* 구조물 종류를 고르면 그 항목 먼저 · 패널 안 설명 글 0
|
||||
* 시작 화면: 아무 B 화면(로그인 · 환경 배정 storage 프로젝트가 현재 프로젝트)
|
||||
* 부르는 법: orca eval --expression "$(cat 이 파일)" (async 식 · JSON 글을 돌려줌) · 1분 넘으면 끊기므로
|
||||
* `window.__help542 = ["b02-proj-register", …];` 를 앞에 붙여 네 화면씩 두 번
|
||||
* 쓰는 것: 없음(주소 이동 · 누르기 · 모달 닫기 · 끝나면 처음 주소로)
|
||||
* 파일 고침 반영: 셸이 `resources/help/B04/99_임시.md` 를 만들고(글 A) 부름 → 글 B 로 고치고 다시 부름 →
|
||||
* `temp` 가 A → B 로 바뀌면 통과 · 뒤에 파일 지움(없으면 temp = null)
|
||||
* 기대 수치:
|
||||
* - pages 8 · 줄마다 btn = 1 · first = true(묶음 맨 왼쪽) · inLeft = true(B02 는 아래 단추 줄) ·
|
||||
* modal = true · toc[0] = 「… 설명서」 · text = 그 설명서 제목 글
|
||||
* - B03 toc = 설명서 + 필요한 파일 · 고르는 방법 · 알아 둘 것 · B06 toc 에 「표준 횡단면 설정」
|
||||
* - pipeTopic = 「구조물 · 배관」(B05 에서 배관을 고른 채 [?]) · leftovers = 0(옮긴 글이 패널에 없음)
|
||||
*/
|
||||
(async () => {
|
||||
const w = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
const until = async (fn, ms = 15000) => {
|
||||
for (let t = 0; t < ms; t += 200) {
|
||||
const v = fn();
|
||||
if (v) return v;
|
||||
await w(200);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const MOVED = [
|
||||
"[저장]은 이 폼에 있는 칸만",
|
||||
"에서 관리 — 여기서 넣어도",
|
||||
"사방기술교본",
|
||||
"표준 편절편성 단면 모식도",
|
||||
"L형 측구는 각 횡단면도에서",
|
||||
"끌어 놓으면 확장자로",
|
||||
"shapefile 한 벌",
|
||||
"작업 좌표계는 라이다 PRJ",
|
||||
"칸이 생기기 전의 동작",
|
||||
"소광리 도면 H=2.0",
|
||||
];
|
||||
const start = location.hash;
|
||||
const vis = (n) => n && n.offsetParent !== null;
|
||||
const leftText = () =>
|
||||
document.querySelector(".ui-workflow-overlay__panel--title")?.innerText ?? "";
|
||||
const openModal = async (btn) => {
|
||||
btn.click();
|
||||
const box = await until(() => document.querySelector(".ui-help .ui-help__text h2"));
|
||||
const res = {
|
||||
modal: !!box,
|
||||
toc: [...document.querySelectorAll(".ui-help__link")].map((b) => b.innerText.trim()),
|
||||
text: box?.innerText ?? null,
|
||||
active: document.querySelector(".ui-help__text")?.dataset.helpKey ?? null,
|
||||
};
|
||||
const temp = [...document.querySelectorAll(".ui-help__link")].find((b) =>
|
||||
b.dataset.helpKey?.startsWith("99_"),
|
||||
);
|
||||
if (temp) {
|
||||
temp.click();
|
||||
res.temp = document.querySelector(".ui-help__text p")?.innerText ?? null;
|
||||
}
|
||||
document.querySelector(".ui-help .ui-modal__top button")?.click();
|
||||
await w(200);
|
||||
res.closed = !document.querySelector(".ui-help");
|
||||
return res;
|
||||
};
|
||||
const out = { pages: [] };
|
||||
// orca eval 은 한 번에 1분쯤에서 끊김 — 앞에 `window.__help542 = ["b05-profile", …];` 를 붙이면 그 화면만
|
||||
const routes = window.__help542 ?? [
|
||||
"b02-proj-register",
|
||||
"b03-file-input",
|
||||
"b04-preprocess",
|
||||
"b05-profile",
|
||||
"b06-section",
|
||||
"b07-design-detail",
|
||||
"b08-quantity",
|
||||
"b09-estimation",
|
||||
];
|
||||
let prev = null;
|
||||
for (const route of routes) {
|
||||
location.hash = `#/${route}`;
|
||||
await w(3000); // 불러오는 동안의 임시 틀(B05 등)이 실제 화면으로 바뀔 때까지
|
||||
// 앞 화면 단추가 빠지고 이 화면 단추가 설 때까지
|
||||
const btn = await until(() =>
|
||||
[...document.querySelectorAll(".ui-help-btn")].find((b) => vis(b) && b !== prev),
|
||||
);
|
||||
prev = btn;
|
||||
await w(1500);
|
||||
const row = btn?.parentElement;
|
||||
const res = {
|
||||
page: route.slice(0, 3).toUpperCase(),
|
||||
btn: document.querySelectorAll(".ui-help-btn").length,
|
||||
first: !!row && row.firstElementChild === btn,
|
||||
inLeft: !!btn?.closest(".ui-sidebar-actions") || !!btn?.closest(".b02-proj__actions"),
|
||||
leftovers: MOVED.filter((s) => leftText().includes(s)),
|
||||
};
|
||||
if (btn) Object.assign(res, await openModal(btn));
|
||||
if (route === "b05-profile") {
|
||||
// 배관 하나를 골라 [?] — 구조물 종류 항목이 먼저 열림(이미 골라져 있으면 그대로 · 끝나면 처음 상태로)
|
||||
const picked = () => document.querySelector('[data-help-topic="구조물_배관"]');
|
||||
const was = !!picked();
|
||||
const item = await until(() =>
|
||||
[...document.querySelectorAll(".b05-route__irregular-item")].find((li) =>
|
||||
li.innerText.includes("배관"),
|
||||
),
|
||||
);
|
||||
if (!was) item?.click();
|
||||
await until(picked, 8000);
|
||||
await w(800);
|
||||
res.leftoversPipe = MOVED.filter((s) => leftText().includes(s));
|
||||
const again = await openModal([...document.querySelectorAll(".ui-help-btn")].find(vis));
|
||||
res.pipeTopic = again.text;
|
||||
if (!was) item?.click(); // 고름 풂
|
||||
}
|
||||
out.pages.push(res);
|
||||
}
|
||||
location.hash = start;
|
||||
return JSON.stringify(out);
|
||||
})();
|
||||
Reference in New Issue
Block a user