// 원본 갑지(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 `
${[
["docf-list", "양식목록"],
["docf-save", "양식저장하기"],
["docf-default", "기본양식으로 저장"],
["docf-init", kind === "gabgi" ? "갑지초기화" : "표지초기화"],
["docf-print", "인쇄"],
["docf-detail", "상세편집"],
]
.map(([a, l]) => ``)
.join("")}
`;
}
// 처음 여는 갑지·표지에 기본양식을 채움(저장은 사용자가 [저장])
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) => ``).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" ? "갑지" : "표지"} 상세편집`,
`${EstDocEditor.clean(html)}
`,
);
} 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 };
})();