Files
Aislo/tools/est-test/native-doc-forms.js
T

179 lines
7.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 원본 갑지(TFRMGABGI)·표지(TFRMCOVER) 팝업 — 양식저장하기·양식불러오기·양식목록(popupSaveFormat·popupLoadFormat·popupFormatTableS),
// 기본양식으로 저장(popupSaveToDefault: 새 공사에서 갑지·표지를 처음 열 때 채움), 갑지·표지 초기화(popupInit), 인쇄(popupPrint).
// 양식은 서버 양식 보관함 종류 document 에 { docKind, fields } 로 둔다(공사명은 양식에 넣지 않음 — 늘 현재 공사 이름).
const EstDocForms = (() => {
const DEFAULT = { gabgi: "기본양식-갑지", cover: "기본양식-표지" };
const FIELDS = {
gabgi: ["Position", "CompanyName", "Date", "CompanyPos", "Explan", "Cost", "Gulga"],
cover: ["Name2", "Date", "CompanyName", "CompanyPos"],
};
const post = (body) => ({
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
const field = (name) => document.querySelector(`#dialog-content [name="doc-${name}"]`);
function read(kind) {
return Object.fromEntries(
FIELDS[kind]
.filter((k) => field(k))
.map((k) => {
const v = field(k).value;
return [
k,
k === "Gulga"
? v
.split(/\r?\n/)
.map((l) => l.trim())
.filter(Boolean)
: k === "CompanyPos"
? Number(v)
: v,
];
}),
);
}
function write(kind, fields) {
for (const k of FIELDS[kind]) {
const node = field(k);
if (!node || fields[k] === undefined) continue;
node.value = Array.isArray(fields[k]) ? fields[k].join("\n") : fields[k];
}
}
async function list(kind) {
return (await EstApp.api("/api/forms")).filter(
(f) => f.kind === "document" && f.data?.docKind === kind,
);
}
function panel(html) {
let box = document.getElementById("doc-forms-panel");
if (!box) {
box = document.createElement("div");
box.id = "doc-forms-panel";
box.className = "business-tools";
document.querySelector("#dialog-content .form-fields")?.before(box);
}
box.innerHTML = html;
}
function tools(kind) {
return `<div class="business-tools">${[
["docf-list", "양식목록"],
["docf-save", "양식저장하기"],
["docf-default", "기본양식으로 저장"],
["docf-init", kind === "gabgi" ? "갑지초기화" : "표지초기화"],
["docf-print", "인쇄"],
["docf-detail", "상세편집"],
]
.map(([a, l]) => `<button data-action="${a}" data-kind="${kind}">${l}</button>`)
.join("")}</div>`;
}
// 처음 여는 갑지·표지에 기본양식을 채움(저장은 사용자가 [저장])
async function fill(state, kind) {
if (state.project.docs?.[kind]) return;
const f = (await list(kind)).find((x) => x.name === DEFAULT[kind]);
if (f) write(kind, f.data.fields);
}
async function run(name, state, node) {
const kind = node.dataset.kind,
esc = EstApp.esc;
if (name === "docf-list") {
const forms = await list(kind);
panel(
forms.length
? `양식목록 ${forms.map((f) => `<button data-action="docf-load" data-kind="${kind}" data-name="${esc(f.name)}">${esc(f.name)}</button><button data-action="docf-delete" data-kind="${kind}" data-name="${esc(f.name)}" title="지우기">×</button>`).join(" ")}`
: "저장한 양식이 없습니다.",
);
} else if (name === "docf-load") {
const f = (await list(kind)).find((x) => x.name === node.dataset.name);
if (!f) throw Error("양식이 없습니다.");
write(kind, f.data.fields);
panel(`「${esc(f.name)}」 양식을 불러왔습니다. [저장]을 누르면 반영됩니다.`);
} else if (name === "docf-delete") {
if (!confirm(`「${node.dataset.name}」 양식을 지우시겠습니까?`)) return;
await EstApp.api("/api/forms/delete", post({ kind: "document", name: node.dataset.name }));
return run("docf-list", state, node);
} else if (name === "docf-save" || name === "docf-default") {
const fname = name === "docf-default" ? DEFAULT[kind] : prompt("양식 이름", "")?.trim();
if (!fname) return;
await EstApp.api(
"/api/forms",
post({ kind: "document", name: fname, data: { docKind: kind, fields: read(kind) } }),
);
panel(`「${esc(fname)}」 양식으로 저장했습니다.`);
} else if (name === "docf-init") {
if (!confirm(`${kind === "gabgi" ? "갑지" : "표지"}를 처음 상태로 되돌리시겠습니까?`)) return;
EstApp.byId("dialog").close();
EstApp.change((project) => {
if (project.docs) delete project.docs[kind];
}, false);
EstDocs.handle("doc-edit", state, { dataset: { kind } });
} else if (name === "docf-detail") {
// 상세편집: 인쇄 모양을 자유 편집(원본은 미리보기 ↔ 편집 전환). 저장하면 인쇄가 이 모양을 씀, 「상세편집 해제」로 칸 값 모양으로
const doc = state.project.docs?.[kind] || {};
const html = doc.detailHtml || EstDocs.html(kind === "gabgi" ? 8 : 9, state.project);
EstApp.showDialog(
`${kind === "gabgi" ? "갑지" : "표지"} 상세편집`,
`<div id="doc-detail" contenteditable="true" style="min-height:360px;max-height:480px;overflow:auto;border:1px solid #bbb;padding:8px;background:#fff">${EstDocEditor.clean(html)}</div><button data-action="docf-detail-save" data-kind="${kind}">저장</button> <button data-action="docf-detail-clear" data-kind="${kind}">상세편집 해제</button>`,
);
} else if (name === "docf-detail-save" || name === "docf-detail-clear") {
const html =
name === "docf-detail-save"
? EstDocEditor.clean(document.getElementById("doc-detail").innerHTML)
: null;
EstApp.byId("dialog").close();
EstApp.change((project) => {
project.docs ||= {};
project.docs[kind] = { ...(project.docs[kind] || {}) };
if (html) project.docs[kind].detailHtml = html;
else delete project.docs[kind].detailHtml;
}, false);
state.notice = html
? "상세편집 모양을 저장했습니다. 인쇄에 그대로 쓰입니다."
: "상세편집을 풀었습니다.";
} else if (name === "docf-print") {
EstApp.byId("dialog").close();
await EstReport.open();
const select = document.getElementById("print-form");
select.value = kind === "gabgi" ? "8" : "9";
select.dispatchEvent(new Event("change", { bubbles: true }));
}
}
const names = new Set([
"docf-detail",
"docf-detail-save",
"docf-detail-clear",
"docf-list",
"docf-load",
"docf-delete",
"docf-save",
"docf-default",
"docf-init",
"docf-print",
]);
function handle(name, state, node) {
if (!names.has(name)) return false;
run(name, state, node).catch((error) => panel(EstApp.esc(error.message)));
return true;
}
// 설계일자 선택(TfrmMonthDate 0xda6438): 갑지 「{년}년 {월}월 일 설계」(일은 빈칸) · 견적서 「{년}년 {월}월 {일}일」
function dateText(value, format) {
const [y, m, d] = String(value).split("-").map(Number);
if (!y || !m) return "";
return format === "estimate" ? `${y}년 ${m}월 ${d}일` : `${y}년 ${m}월 일 설계`;
}
if (typeof document !== "undefined" && document.addEventListener)
document.addEventListener("change", (event) => {
const t = event.target;
if (t.dataset?.dateTarget === undefined || !t.value) return;
const target = document.querySelector(`#dialog-content [name="${t.dataset.dateTarget}"]`);
if (!target) return;
target.value = dateText(t.value, t.dataset.dateFormat);
target.dispatchEvent(new Event("input", { bubbles: true }));
target.dispatchEvent(new Event("change", { bubbles: true }));
});
return { tools, fill, dateText, handle };
})();