fix(est): preserve active edits during queued saves
This commit is contained in:
@@ -8,6 +8,8 @@
|
||||
|
||||
저장은 이 폴더의 `data/`에 작업을 보관하고 `.estweb.json` 파일도 내려받습니다. 브라우저 임시 저장은 같은 탭에서 복원하는 용도입니다. ESTX는 읽기 전용으로 가져오며 원본 압축 파일과 바이너리를 보존합니다. 편집 결과를 원본 ESTX로 다시 쓰는 기능은 없습니다.
|
||||
|
||||
Ctrl+S는 입력 중인 셀도 확정한 뒤 저장합니다. 연속 저장 요청은 순서대로 처리하며, 저장 대기 중의 추가 입력이나 새로 연 문서를 이전 저장 응답으로 덮어쓰지 않습니다. 실행 취소는 현재 작업 파일의 연결을 유지합니다.
|
||||
|
||||
초기 자료는 기술정보 DB의 실제 REA 저장 샘플입니다. `REA 샘플 대조`에서 원본 저장값과 275개 계산 결과를 비교합니다. 테스트용 임의 요율을 원본 기본값으로 넣지 않습니다. 새 원가 서식의 요율은 사용자가 입력합니다.
|
||||
|
||||
## 검증과 남은 범위
|
||||
|
||||
@@ -33,7 +33,10 @@ const EstActions = (() => {
|
||||
target = redo ? state.history : state.future;
|
||||
if (!source.length) return;
|
||||
target.push(JSON.stringify(state.project));
|
||||
const id = state.project.id;
|
||||
state.project = JSON.parse(source.pop());
|
||||
if (id) state.project.id = id;
|
||||
state.revision++;
|
||||
state.selection.clear();
|
||||
EstApp.cache();
|
||||
EstApp.render();
|
||||
|
||||
@@ -11,7 +11,10 @@ const EstApp = (() => {
|
||||
future: [],
|
||||
notice: "",
|
||||
business: null,
|
||||
revision: 0,
|
||||
documentKey: 0,
|
||||
};
|
||||
let saveQueue = Promise.resolve();
|
||||
const titles = {
|
||||
EstMainDat: "내역서",
|
||||
HeadMainDat: "일위대가",
|
||||
@@ -45,14 +48,15 @@ const EstApp = (() => {
|
||||
if (!response.ok) throw Error(data.error || `요청 실패: ${response.status}`);
|
||||
return data;
|
||||
}
|
||||
function cache() {
|
||||
function cache(serialized) {
|
||||
try {
|
||||
sessionStorage.setItem(CACHE, JSON.stringify(state.project));
|
||||
sessionStorage.setItem(CACHE, serialized ?? JSON.stringify(state.project));
|
||||
} catch {
|
||||
state.notice = "임시 저장 용량이 부족합니다. 저장 버튼으로 작업을 보관하세요.";
|
||||
}
|
||||
}
|
||||
function remember() {
|
||||
state.revision++;
|
||||
const serialized = JSON.stringify(state.project);
|
||||
if (serialized.length < 10 * 1024 * 1024) {
|
||||
state.history.push(serialized);
|
||||
@@ -61,6 +65,7 @@ const EstApp = (() => {
|
||||
state.future = [];
|
||||
}
|
||||
function calculate() {
|
||||
state.revision++;
|
||||
try {
|
||||
state.project = EstNative.calculate(EstModel.validate(state.project));
|
||||
} catch (error) {
|
||||
@@ -76,6 +81,8 @@ const EstApp = (() => {
|
||||
}
|
||||
function setProject(project) {
|
||||
state.project = EstModel.legacy(project);
|
||||
state.documentKey++;
|
||||
state.revision++;
|
||||
state.history = [];
|
||||
state.future = [];
|
||||
state.selection.clear();
|
||||
@@ -180,18 +187,40 @@ const EstApp = (() => {
|
||||
a.click();
|
||||
setTimeout(() => URL.revokeObjectURL(a.href), 1000);
|
||||
}
|
||||
async function save(asNew = false) {
|
||||
if (asNew) delete state.project.id;
|
||||
const result = await api("/api/save", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(state.project),
|
||||
});
|
||||
state.project = result;
|
||||
cache();
|
||||
state.notice = "저장했습니다. 이 앱 폴더의 data에도 작업이 보관됩니다.";
|
||||
render();
|
||||
download(`${state.project.name || "내역서"}.estweb.json`, JSON.stringify(result, null, 2));
|
||||
function save(asNew = false) {
|
||||
document.activeElement?.blur?.();
|
||||
if (asNew) {
|
||||
delete state.project.id;
|
||||
state.documentKey++;
|
||||
}
|
||||
state.project.id ||= [...crypto.getRandomValues(new Uint8Array(12))]
|
||||
.map((value) => value.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
const documentKey = state.documentKey,
|
||||
revision = state.revision,
|
||||
body = JSON.stringify(state.project);
|
||||
cache(body);
|
||||
const operation = saveQueue
|
||||
.catch(() => {})
|
||||
.then(async () => {
|
||||
const result = await api("/api/save", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body,
|
||||
});
|
||||
if (state.documentKey === documentKey) {
|
||||
if (state.revision === revision) state.project = result;
|
||||
cache();
|
||||
state.notice =
|
||||
state.revision === revision
|
||||
? "저장했습니다. 이 앱 폴더의 data에도 작업이 보관됩니다."
|
||||
: "요청 시점의 작업을 저장했습니다. 이후 입력은 다시 저장하세요.";
|
||||
render();
|
||||
}
|
||||
download(`${result.name || "내역서"}.estweb.json`, JSON.stringify(result, null, 2));
|
||||
});
|
||||
saveQueue = operation;
|
||||
return operation;
|
||||
}
|
||||
async function open(file) {
|
||||
if (!file) return;
|
||||
|
||||
Reference in New Issue
Block a user