- 새 M02_Doc_Match(서버 단독) — 후보(결과단위 · 노임 키 · 재료 · 기계 · 품셈 절 번호) · 옵션 풀이(줄 수량 · 단가가 의존하는 선택 · 입력 변수만 돌려 좁힘 · 글 근거 값 유지) · 줄 대조(같음 · 단가 차이 · 단위 환산 · 기계 규격 · 비목 · 율 값 · 율 줄 · 공구손료 유무 · 줄 없음 · 끝수 · 옵션 글) · 판정 확정(다른 항 0 · 원 단위 같음) / 근접 / 없음(가까운 후보 보이기만)
- 새 M02_Doc_MatchSk — 찾기 = SK 가져온.원본.연결후보 적음 · 바꾸기 = 서버가 다시 찾아 확정만 같은 키에 M01 사본 SK(바탕 · 입력값 = 찾은 옵션) · 원래 몸 = 가져온보관 · 되돌리기 · 근접 · 없음 409
- 길 POST /api/m02/layers/{층}/sangeun/{키}/match {할일 찾기 · 바꾸기 · 되돌리기}(층 쓰기)
- M02_Sangeun_Imported clean — 가져온보관 칸 받음 · M02_Doc_Import 형제 SK 가 우리 기초 사본이면 그 수식 문서로 셈(연결 안 끊김)
- 화면 — 원문 패널 「어디서 왔나」 카드 맨 위 판정 띠(띠 알림 부품) · 확정 = [우리 기초 사본으로 바꾸기] · 근접 · 없음 = 단추 없음 + 다른 항 자세히 · 안 찾음 = [우리 기초와 연결 찾기] · 바꾼 SK = [가져온 식으로 되돌리기] · 저장 안 한 고침 있으면 막음 · 끝나면 SK 다시 엶(새 판)
- 시험 test_810_4_match(판정 셋 · 요구 704 파형강관 Φ800 = GF000179 근접 · 단위 환산 × 6 · 기계 규격 · 율 줄 · 옵션 풀이 · 줄 대조 · 바꾸기 = M01 사본 셈 · 되돌리기 · 근접 409 · 형제 사본 · 전수 S8104_ALL) · helper_810_4_sweep
- 낡은 시험 글 대조 고침 — 717_2b(View 다시 엶 mount) · 750_2(createSangeunSide(opt.match)) · 807_1(원문 패널에 SK 문서 넘김)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
86 lines
3.9 KiB
TypeScript
86 lines
3.9 KiB
TypeScript
/* =============================================================================
|
|
* M02_MasterTemplete_Sangeun_MatchBand.ts
|
|
* 가져온 식 ↔ 우리 기초 판정 띠(PLAN 810-4 · 요구 707 · 709 · 703) — 원문 패널 「어디서 왔나」 카드 맨 위.
|
|
* 글 · 단추 = `logic/M02_Sangeun_Match.ts` `matchBand` · 길 POST `/m02/layers/{층}/sangeun/{키}/match`
|
|
* {할일 찾기 | 바꾸기 | 되돌리기} — 서버가 다시 찾아 「확정」 일 때만 바꿈(화면 판정을 믿지 않음) ·
|
|
* 바꾸기 · 되돌리기 = 확인창 · 저장 안 한 고침이 있으면 막음(먼저 [저장]) · 끝나면 `done(판)` = 그 SK 를 다시 엶
|
|
* 층 쓰기 권한 없음(읽기만) = 단추 없음
|
|
* ========================================================================== */
|
|
|
|
import { createBanner } from "@ui/ui_template_banner";
|
|
import { createButton } from "@ui/ui_template_button";
|
|
import { el } from "@ui/ui_template_elements";
|
|
import { askConfirm } from "@ui/ui_template_modal";
|
|
import { createText } from "@ui/ui_template_text";
|
|
import { showToast } from "@ui/ui_template_toast";
|
|
import { call, type Layer } from "./M02_MasterTemplete_Api_Fetch";
|
|
import { matchBand, type MatchAction } from "./logic/M02_Sangeun_Match";
|
|
|
|
export interface MatchCtx {
|
|
layer: Layer;
|
|
owner: string | null;
|
|
key: string;
|
|
readOnly: boolean;
|
|
/** 저장 안 한 고침 있음 */
|
|
dirty: () => boolean;
|
|
/** 서버가 SK 를 고친 뒤(새 판) — 그 SK 다시 엶 */
|
|
done: (version: string) => void;
|
|
}
|
|
|
|
const LABEL: Record<Exclude<MatchAction, null>, string> = {
|
|
찾기: "우리 기초와 연결 찾기",
|
|
바꾸기: "우리 기초 사본으로 바꾸기",
|
|
되돌리기: "가져온 식으로 되돌리기",
|
|
};
|
|
const ASK: Partial<Record<Exclude<MatchAction, null>, string>> = {
|
|
바꾸기:
|
|
"이 단가산출을 우리 기초 사본(찾은 옵션)으로 바꿈 — 키 그대로 · 일위대가 금액이 다시 셈됨 · 원래 가져온 식은 보관",
|
|
되돌리기: "우리 기초 사본을 원래 가져온 식으로 되돌림 — 가져온 식 금액 그대로",
|
|
};
|
|
|
|
/** 판정 띠 — 가져온 식 · 바꾼 SK 가 아니면 null */
|
|
export function createMatchBand(sk: unknown, ctx: MatchCtx | undefined): HTMLElement | null {
|
|
const band = matchBand(sk as Parameters<typeof matchBand>[0]);
|
|
if (!band) return null;
|
|
const act = band.action;
|
|
const actions =
|
|
act && ctx && !ctx.readOnly && ctx.key
|
|
? [
|
|
createButton({
|
|
label: LABEL[act],
|
|
variant: act === "바꾸기" ? "filled" : "ghost",
|
|
size: 24,
|
|
onClick: (ev) => void run(act, ctx, ev.currentTarget as HTMLButtonElement),
|
|
}),
|
|
]
|
|
: [];
|
|
const details = band.details.length
|
|
? el("div", { children: band.details.map((x) => createText(x, "muted", 12)) })
|
|
: undefined;
|
|
const root = createBanner({ tone: band.tone, text: band.text, details, actions });
|
|
root.setAttribute("data-sk-match", act ?? "");
|
|
return root;
|
|
}
|
|
|
|
async function run(act: Exclude<MatchAction, null>, ctx: MatchCtx, btn: HTMLButtonElement) {
|
|
if (ctx.dirty()) return void showToast("저장 안 한 고침이 있음 — 먼저 [저장]", "warning");
|
|
const ask = ASK[act];
|
|
if (ask && !(await askConfirm({ message: ask, okLabel: LABEL[act] }))) return;
|
|
const label = btn.textContent;
|
|
btn.disabled = true;
|
|
btn.textContent = act === "찾기" ? "찾는 중…" : "바꾸는 중…";
|
|
try {
|
|
const owner = ctx.owner ? `?owner=${encodeURIComponent(ctx.owner)}` : "";
|
|
const path = `/layers/${ctx.layer}/sangeun/${encodeURIComponent(ctx.key)}/match${owner}`;
|
|
const got = await call<{ 판?: string }>(path, {
|
|
method: "POST",
|
|
body: JSON.stringify({ 할일: act }),
|
|
});
|
|
ctx.done(got.판 ?? "");
|
|
} catch (e) {
|
|
showToast(e instanceof Error ? e.message : String(e), "error");
|
|
btn.disabled = false;
|
|
btn.textContent = label;
|
|
}
|
|
}
|