- M02 SK 「수식」 탭 = M01 과 같은 공용 화면(`createDocScreen` · 길 앞머리만 `/m02/layers/{층}/sangeun/{키}/doc`) — 옛 M02 수식 나무 화면(`_Sangeun_Equation.ts` · 796-2b · 2c · 2e · 800 · 801 꼴) 걷음
· 옵션 = SK 문서 값(저장) · [단가 갱신] · [재생성](미리 셈 → 알림 계 전 → 후 · 버리는 것 → 저장) · 「M01 새 판 있음」 배지(`formula_doc_copy.ts`)
· 단가산출 연결 펼침 머리 「연결 / 끊음」 · [연결 끊기] · [다시 잇기] · 끊은 문서 안 줄 그 자리 고침(`formula_doc_sub.ts`) · 저장 몸 {문서, 끊음, 판}
· 층 쓰기 권한 없음 = 읽기만(옵션 잠금 · 단추 없음 · 「읽기만」) · 판 바뀜 409 = 사본 같으면 새 판으로 한 번 더 · 저장 뒤 페이지 판 · 상세 탭 `수식` 칸 새로
· 저장 안 한 SK(일위대가 줄 모달) = 「저장한 단가산출만」 글
- 서버 — 수식 길 답에 `연결`(화면 캐시 기록 · `M02_Doc_Screen.record`) · 연결 후보 · 표 길(층 읽기 · M01 후보 · 표 그대로 · main 등록)
- 꺼진 줄(요구 663-1 마저) — 곱 인수 하나가 선택으로 꺼진 0(만약 0 갈래 · 식 줄 기호면 그 식으로 거침)이면 숨김 · 입력 · 글 0 은 그대로
· 판정을 순수 파일로(`common_util_formula_doc_view.ts` · Node 로 시험) · 1,518 문서 기본 옵션 꺼짐 158 → 203 줄(꺼진 줄 금액 모두 0) · GC000211 폼타이 아님 = 세 규격 줄 모두
- 화면 파일 700 줄 — 고침 함수 · 옵션 가로 · 연결 펼침을 새 파일로(`formula_doc_edit` · `_options` · `_sub`)
- 시험 test_802_4b2(5 · 헬퍼 helper_802_4b2_off.cjs) · 옛 시험 둘 고침 — test_793_3b · test_796_2b 가 걷은 옛 M02 수식 화면 글을 읽음 → 없음 확인으로
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
90 lines
3.7 KiB
TypeScript
90 lines
3.7 KiB
TypeScript
/* =============================================================================
|
|
* formula_doc_copy.ts
|
|
* 수식 문서 사본 몫(PLAN 802-4b-2 · 요구 655-2 · 656 · 659-3) — M02 단가산출(SK) 수식 탭 머리
|
|
* 「M01 새 판 있음」 배지(값 줄 · 부름 문서가 지금 M01 과 다름) · [단가 갱신](자원 단가만 · 식 그대로) ·
|
|
* [재생성](지금 M01 문서로 다시 찍음 · 버리는 것 알림) — 누를 때만(자동 갱신 없음)
|
|
* 흐름 = 미리 셈(`{길}/preview` {할일}) → 확인 창(계 전 → 후 · 바뀐 자원 · 버리는 것) → 저장(`{길}/save` {할일, 판})
|
|
* 층 쓰기 권한 없으면 「읽기만」 배지
|
|
* ========================================================================== */
|
|
|
|
import { createBadge } from "@ui/ui_template_badge";
|
|
import { createButton } from "@ui/ui_template_button";
|
|
import { eqText } from "@ui/ui_template_equation";
|
|
import { showConfirmDialog } from "@ui/ui_template_elements";
|
|
import { ft } from "./formula_doc_text";
|
|
|
|
export type CopyJob = "단가갱신" | "재생성";
|
|
|
|
/** 사본 표지(서버 `M02_Doc_Copy.state` + 시점 · 따로) */
|
|
export interface CopyMark {
|
|
M01새판?: boolean;
|
|
지운키?: string[];
|
|
단가새값?: boolean;
|
|
문서새판?: string[];
|
|
따로?: boolean;
|
|
}
|
|
|
|
export interface CopyOptions {
|
|
/** SK 키 · 꼬리(`""` · `/preview` · `/table?key=…`) → 길(`/m02/layers/{층}/sangeun/{키}/doc…` · 주인 물음) */
|
|
route: (key: string, path: string) => string;
|
|
/** 저장 뒤 그 SK 새 판(페이지 [저장] 409 막음) */
|
|
onSaved?: (version: string) => void;
|
|
}
|
|
|
|
type Money = Record<string, number | null> | null;
|
|
|
|
export interface JobReply {
|
|
전?: Money;
|
|
후?: Money;
|
|
바뀐자원?: unknown[];
|
|
버림?: { 문서고침?: boolean; 끊음?: string[] };
|
|
}
|
|
|
|
/** 「M01 새 판 있음」 — 값 줄 · 단가 · 부름 문서 어느 하나라도 지금 M01 과 다름 */
|
|
export function isStale(m: CopyMark | undefined): boolean {
|
|
return !!m && (!!m.M01새판 || !!m.단가새값 || !!m.지운키?.length || !!m.문서새판?.length);
|
|
}
|
|
|
|
export function copyBadges(m: CopyMark | undefined, edit: boolean): HTMLElement[] {
|
|
const out: HTMLElement[] = [];
|
|
if (isStale(m)) {
|
|
const b = createBadge(ft("New_M01"), "danger");
|
|
b.dataset.copyStale = "";
|
|
out.push(b);
|
|
}
|
|
if (!edit) out.push(createBadge(ft("Read_Only"), "neutral"));
|
|
return out;
|
|
}
|
|
|
|
export function copyTools(edit: boolean, run: (job: CopyJob) => void): HTMLElement[] {
|
|
if (!edit) return [];
|
|
return (["단가갱신", "재생성"] as const).map((job) => {
|
|
const b = createButton({
|
|
label: job === "단가갱신" ? ft("Price") : ft("Regen"),
|
|
variant: "ghost",
|
|
size: 28,
|
|
onClick: () => run(job),
|
|
});
|
|
b.dataset.copyJob = job;
|
|
return b;
|
|
});
|
|
}
|
|
|
|
const sum = (m: Money | undefined): string =>
|
|
typeof m?.계 === "number" ? `${eqText(m.계)} 원` : "—";
|
|
|
|
/** 할일 알림 — 계 전 → 후 · 바뀐 자원 수 · 재생성이 버리는 것(이름) → 확인이면 true */
|
|
export function askJob(job: CopyJob, got: JobReply, names: Map<string, string>): Promise<boolean> {
|
|
const bits = [
|
|
job === "단가갱신" ? ft("Price_Ask") : ft("Regen_Ask"),
|
|
`${ft("Before_After")} ${sum(got.전)} → ${sum(got.후)}`,
|
|
];
|
|
if (job === "단가갱신") bits.push(`${ft("Changed")} ${got.바뀐자원?.length ?? 0}`);
|
|
const lost = [
|
|
...(got.버림?.문서고침 ? [ft("Regen_Edit")] : []),
|
|
...(got.버림?.끊음 ?? []).map((k) => `${ft("Cut")} ${names.get(k) || k}`),
|
|
];
|
|
if (job === "재생성" && lost.length) bits.push(`${ft("Regen_Lost")} — ${lost.join(" · ")}`);
|
|
return showConfirmDialog(bits.join(" · "), job === "단가갱신" ? ft("Price") : ft("Regen"));
|
|
}
|