- [수정] 모달 = Labor_Table(읽기 · 덧칸 표지 · 엑셀 금액) · 줄 = 로직(단가산출 · 일위대가) / 요소(연결됨 · 후보 · 못 이음) · 단가산출 줄 = 가져온 식 SK 수식 탭 모달(Labor_SkEdit) · 서버 hopyo-view(M02_Hopyo_View) - 엑셀 보기 목록 · 칠 = 줄 표지(View.listing) · 단가산출 탭 = 블록 목록 + [가져온 식으로 열기] · [내 단가산출로] - 걷음(요구 703): 판정 체계 785(Judge · Solve · Bind · San_Basis 다섯 · Make.Move · Row 1차 판정 · 화면 Check · CheckRows · Rows · Block · JudgeApi · Picks · Judge.css) · [우리 기초로 바꾸기](to-base) · 근거 기초 제안 · Clause 기초 판정 - 시험 지움: 642_2(Bind · Solve) · 642_3(Row 1차 판정) · 642_5(옮기기 Move) · 642_6(판정 화면) · 786_2(옛 [수정] 모달) · 793_3a(근거 → 기초 제안) — 모두 걷은 기초 판정 길 몫 - 시험 고침: 642_1 · 794_2a2 · 794_2b(근거 제안 부분만 뺌) · 793_2 · 793_3b(기초 바꾸기 기대 → 없음) · 627 · 779_2 · 785_4 · 807_1 · m02_hopyo_excel · grid · stmate(judge 길 → view 길) - 새 시험 test_809_1_import_labor(기초 길 0 · 표지 1,350 줄 · 가져온 식 SK · 모달 = Labor_Table · 금액 = 엑셀) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
127 lines
5.6 KiB
TypeScript
127 lines
5.6 KiB
TypeScript
/* =============================================================================
|
|
* M02_MasterTemplete_Hopyo_Import.ts
|
|
* 일위대가 불러오기 본 칸 보기(PLAN 785-4 · 요구 642-11 — 마법사 `Hopyo_Wizard` 갈음 · 가운데 상세 페이지 없음) —
|
|
* 왼쪽 [불러오기] → 층 · 파일 고름 → 올림(`Hopyo_Side`) → 바로 이 보기:
|
|
* · 머리 막대 = 엑셀 보기 몸(`Hopyo_Sheet` — 시트 · 호표 · 검산 · 연결)의 머리에 층 · [끝내기]를 덧붙임
|
|
* · 아래 = 엑셀(탭 셋) + 오른쪽 호표 · 단가산출 목록(`Hopyo_Sheet` · `Hopyo_SheetSk`) · 만들기 = 호표 줄 [내 일위대가로](807-1)
|
|
* · [끝내기] = 물음(안 만든 단가산출 알림) → 서버가 이번 HP · 격자 저장본 지움 ·
|
|
* 만든 일위대가 · 단가산출(엑셀 사본 걷고 출처만) · 별칭 장부 남음 · 옛 「안 이은 줄」 띠는 785-7 에서 지움(판정 「고를 것」)
|
|
* · 작업 중 = 이 화면 동안만(743-1) — 화면 떠남(`leave` · 페이지 닫음) = 이 화면에서 올리거나 이은 파일 지움 ·
|
|
* 하루 지난 것은 서버가 지움 · 엑셀 자료는 보관하지 않음(필요하면 다시 올림)
|
|
* ========================================================================== */
|
|
|
|
import { createBadge } from "@ui/ui_template_badge";
|
|
import { createButton, el, showConfirmDialog, showToast } from "@ui/ui_template_elements";
|
|
import { t as L } from "@ui/ui_template_locale";
|
|
import { createText } from "@ui/ui_template_text";
|
|
import { fetchSessionUser } from "../A06_Login/A06_Login_Api_Fetch";
|
|
import { deleteHopyo, type HopyoFile, type HopyoScope } from "./M02_MasterTemplete_Hopyo_Api";
|
|
import type { HopyoSheetBody } from "./M02_MasterTemplete_Hopyo_Sheet";
|
|
import { accessFor, type ManageLayer } from "./M02_MasterTemplete_Layer";
|
|
import "./M02_MasterTemplete_Hopyo.css";
|
|
|
|
export const LAYERS: { layer: ManageLayer; text: string }[] = [
|
|
{ layer: "personal", text: "개인" },
|
|
{ layer: "company", text: "회사" },
|
|
{ layer: "system", text: "마스터" },
|
|
];
|
|
export const layerText = (layer: string): string =>
|
|
LAYERS.find((x) => x.layer === layer)?.text ?? layer;
|
|
|
|
const why = (error: unknown): string => (error instanceof Error ? error.message : String(error));
|
|
|
|
/** 쓸 수 있는 자기 층(개인 · 회사 · 마스터 차례) */
|
|
export async function writableLayers(): Promise<ManageLayer[]> {
|
|
const user = await fetchSessionUser().catch(() => null);
|
|
return LAYERS.map((x) => x.layer).filter((layer) => accessFor(layer, null, user).canWrite);
|
|
}
|
|
|
|
export interface HopyoImportArgs {
|
|
layer: ManageLayer;
|
|
file: HopyoFile;
|
|
/** [끝내기] 로 올린 파일을 지웠음 */
|
|
onEnded: () => void;
|
|
}
|
|
|
|
export interface HopyoImport {
|
|
root: HTMLElement;
|
|
/** 화면 떠남 — 이 화면에서 올리거나 이은 파일 지움(743-1) */
|
|
leave: () => void;
|
|
/** 안 옮긴 고름 — 판정 고름 캐시 걷음(809-1) 뒤 늘 거짓 */
|
|
dirty: () => boolean;
|
|
}
|
|
|
|
export function buildHopyoImport(a: HopyoImportArgs): HopyoImport {
|
|
const { layer, file } = a;
|
|
const scope: HopyoScope = { layer, owner: null };
|
|
let held = true;
|
|
let body: HopyoSheetBody | null = null;
|
|
|
|
const root = el("div", {
|
|
className: "m02-hopyo-import",
|
|
attrs: { "data-hopyo-import": file.키 },
|
|
children: [createText("엑셀을 읽는 중…", "muted", 14)],
|
|
});
|
|
const leave = (): void => {
|
|
window.removeEventListener("pagehide", leave);
|
|
if (!held) return;
|
|
held = false;
|
|
void deleteHopyo(scope, file.키, true).catch(() => {});
|
|
body?.destroy();
|
|
};
|
|
window.addEventListener("pagehide", leave);
|
|
|
|
// ── 머리 막대에 덧붙일 것 ──
|
|
const finish = createButton({ label: "끝내기", variant: "filled", size: 28, onClick: () => void end() }); // prettier-ignore
|
|
finish.dataset.hopyoFinish = "";
|
|
const layerNote = createBadge(`${layerText(layer)} 층`, "neutral");
|
|
|
|
async function end(): Promise<void> {
|
|
finish.disabled = true;
|
|
const w = body?.skCount(); // 안 옮긴 단가산출(일위대가 밖 포함) — 묻기만
|
|
const ok = await showConfirmDialog(
|
|
`「${file.파일}」 불러오기를 끝냄 — 올린 파일(엑셀 결과 · 격자 · 블록 풀이)을 지움 · 만든 일위대가 · 단가산출(출처만 남김)과 별칭 장부는 남음 · 같은 엑셀은 필요하면 다시 올림` +
|
|
(w?.블록
|
|
? ` · ${L("M02_SkUnmoved").replace("{n}", String(w.블록)).replace("{m}", String(w.밖))}`
|
|
: ""), // 780-2 막지 않음
|
|
"끝내기",
|
|
);
|
|
if (!ok) return void (finish.disabled = false);
|
|
try {
|
|
const out = (await deleteHopyo(scope, file.키)) as { 만든?: number };
|
|
held = false;
|
|
window.removeEventListener("pagehide", leave);
|
|
body?.destroy();
|
|
showToast(`끝냄 — 만든 일위대가 · 단가산출 ${out.만든 ?? 0} · 올린 파일 지움`, "success");
|
|
} catch (error) {
|
|
showToast(why(error), "error");
|
|
finish.disabled = false;
|
|
return;
|
|
}
|
|
a.onEnded();
|
|
}
|
|
|
|
// ── 몸 — 엑셀 보기 몸을 바로 담음(스프레드시트는 열 때 불러옴) ──
|
|
void import("./M02_MasterTemplete_Hopyo_Sheet")
|
|
.then((m) => {
|
|
if (!held) return;
|
|
const view = m.buildHopyoSheet({
|
|
scope,
|
|
key: file.키,
|
|
파일: file.파일,
|
|
target: scope,
|
|
canWrite: true,
|
|
headExtra: [layerNote, finish],
|
|
});
|
|
body = view;
|
|
root.replaceChildren(...view.parts);
|
|
})
|
|
.catch((error: unknown) => root.replaceChildren(createText(why(error), "muted", 14)));
|
|
|
|
return {
|
|
root,
|
|
leave,
|
|
dirty: () => false,
|
|
};
|
|
}
|