refactor
This commit is contained in:
267
lib/editor/section/useSectionCommands.ts
Normal file
267
lib/editor/section/useSectionCommands.ts
Normal file
@@ -0,0 +1,267 @@
|
||||
import { useCallback } from "react";
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
import { ApiError } from "@/api/http";
|
||||
import {
|
||||
createSection,
|
||||
createSectionCommit,
|
||||
fetchSectionCommits,
|
||||
fetchSections,
|
||||
openSectionEditor,
|
||||
restoreSectionCommit,
|
||||
submitSection,
|
||||
} from "@/api/sections";
|
||||
import { buildEditorSnapshot, normalizeEditorSnapshot } from "@/lib/editor/snapshot/editorSnapshot";
|
||||
import type { Change } from "@/lib/editor/draft/editorTypes";
|
||||
import type { CreatedEntitySummary, PendingEntityCreate } from "@/lib/editor/session/sessionTypes";
|
||||
import type { Feature, FeatureCollection, FeatureId } from "@/types/geo";
|
||||
import type { EditorSnapshot, Section, SectionCommit, SectionState } from "@/types/sections";
|
||||
|
||||
type EditorDraftApi = {
|
||||
draft: FeatureCollection;
|
||||
buildPayload: () => Change[];
|
||||
clearChanges: () => void;
|
||||
hasPersistedFeature: (id: Feature["properties"]["id"]) => boolean;
|
||||
};
|
||||
|
||||
type Options = {
|
||||
editor: EditorDraftApi;
|
||||
editorUserId: string;
|
||||
emptyFeatureCollection: FeatureCollection;
|
||||
activeSection: Section | null;
|
||||
sectionState: SectionState | null;
|
||||
selectedSectionId: string;
|
||||
newSectionTitle: string;
|
||||
pendingSaveCount: number;
|
||||
pendingEntityCreates: PendingEntityCreate[];
|
||||
lastSectionSnapshot: EditorSnapshot | null;
|
||||
commitTitle: string;
|
||||
commitNote: string;
|
||||
setActiveSection: Dispatch<SetStateAction<Section | null>>;
|
||||
setSelectedSectionId: Dispatch<SetStateAction<string>>;
|
||||
setSectionState: Dispatch<SetStateAction<SectionState | null>>;
|
||||
setLastSectionSnapshot: Dispatch<SetStateAction<EditorSnapshot | null>>;
|
||||
setInitialData: Dispatch<SetStateAction<FeatureCollection>>;
|
||||
setSectionCommits: Dispatch<SetStateAction<SectionCommit[]>>;
|
||||
setPendingEntityCreates: Dispatch<SetStateAction<PendingEntityCreate[]>>;
|
||||
setCreatedEntities: Dispatch<SetStateAction<CreatedEntitySummary[]>>;
|
||||
setSelectedFeatureId: Dispatch<SetStateAction<FeatureId | null>>;
|
||||
setEntityFormStatus: Dispatch<SetStateAction<string | null>>;
|
||||
setEntityStatus: Dispatch<SetStateAction<string | null>>;
|
||||
setIsSaving: Dispatch<SetStateAction<boolean>>;
|
||||
setIsSubmitting: Dispatch<SetStateAction<boolean>>;
|
||||
setIsOpeningSection: Dispatch<SetStateAction<boolean>>;
|
||||
setAvailableSections: Dispatch<SetStateAction<Section[]>>;
|
||||
setNewSectionTitle: Dispatch<SetStateAction<string>>;
|
||||
setCommitTitle: Dispatch<SetStateAction<string>>;
|
||||
setCommitNote: Dispatch<SetStateAction<string>>;
|
||||
};
|
||||
|
||||
export function useSectionCommands(options: Options) {
|
||||
const openSectionForEditing = useCallback(async (sectionId: string) => {
|
||||
const editorPayload = await openSectionEditor(sectionId, options.editorUserId);
|
||||
const snapshot = normalizeEditorSnapshot(editorPayload.snapshot);
|
||||
const commits = await fetchSectionCommits(sectionId);
|
||||
const nextInitialData = snapshot?.editor_feature_collection || options.emptyFeatureCollection;
|
||||
|
||||
options.setActiveSection(editorPayload.section);
|
||||
options.setSelectedSectionId(editorPayload.section.id);
|
||||
options.setSectionState(editorPayload.state);
|
||||
options.setLastSectionSnapshot(snapshot);
|
||||
options.setInitialData(nextInitialData);
|
||||
options.setSectionCommits(commits);
|
||||
options.setPendingEntityCreates([]);
|
||||
options.setCreatedEntities([]);
|
||||
options.setSelectedFeatureId(null);
|
||||
options.setEntityFormStatus(null);
|
||||
}, [options]);
|
||||
|
||||
const commitSection = useCallback(async () => {
|
||||
if (!options.activeSection || !options.sectionState) {
|
||||
options.setEntityStatus("Chưa mở được section editor.");
|
||||
return;
|
||||
}
|
||||
|
||||
const geometryChanges = options.editor.buildPayload();
|
||||
options.setIsSaving(true);
|
||||
options.setEntityStatus(null);
|
||||
try {
|
||||
const snapshot = buildEditorSnapshot({
|
||||
section: options.activeSection,
|
||||
draft: options.editor.draft,
|
||||
changes: geometryChanges,
|
||||
pendingEntities: options.pendingEntityCreates,
|
||||
previousSnapshot: options.lastSectionSnapshot,
|
||||
hasPersistedFeature: options.editor.hasPersistedFeature,
|
||||
});
|
||||
const result = await createSectionCommit(options.activeSection.id, {
|
||||
snapshot,
|
||||
created_by: options.editorUserId,
|
||||
expected_version: options.sectionState.version,
|
||||
expected_head_commit_id: options.sectionState.head_commit_id,
|
||||
title: options.commitTitle.trim() || `Commit ${new Date().toLocaleString()}`,
|
||||
note: options.commitNote.trim() || null,
|
||||
});
|
||||
|
||||
options.setSectionState(result.state);
|
||||
options.setLastSectionSnapshot(snapshot);
|
||||
options.setInitialData(options.editor.draft);
|
||||
options.editor.clearChanges();
|
||||
options.setPendingEntityCreates([]);
|
||||
options.setCreatedEntities([]);
|
||||
options.setCommitTitle("");
|
||||
options.setCommitNote("");
|
||||
options.setSectionCommits(await fetchSectionCommits(options.activeSection.id));
|
||||
options.setEntityFormStatus(`Đã tạo commit #${result.commit.commit_no}.`);
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError) {
|
||||
console.error("Commit failed", err.body);
|
||||
options.setEntityStatus(`Commit thất bại: ${err.body}`);
|
||||
return;
|
||||
}
|
||||
console.error("Commit error", err);
|
||||
options.setEntityStatus("Commit thất bại.");
|
||||
} finally {
|
||||
options.setIsSaving(false);
|
||||
}
|
||||
}, [options]);
|
||||
|
||||
const openSelectedSection = useCallback(async () => {
|
||||
const sectionId = options.selectedSectionId.trim();
|
||||
if (!sectionId) {
|
||||
options.setEntityStatus("Hãy chọn section để mở.");
|
||||
return;
|
||||
}
|
||||
if (options.pendingSaveCount > 0) {
|
||||
const confirmed = window.confirm("Section hiện tại có thay đổi chưa Commit. Mở section khác sẽ bỏ các thay đổi này. Tiếp tục?");
|
||||
if (!confirmed) return;
|
||||
}
|
||||
|
||||
options.setIsOpeningSection(true);
|
||||
options.setEntityStatus(null);
|
||||
try {
|
||||
await openSectionForEditing(sectionId);
|
||||
options.setEntityStatus("Đã mở section để chỉnh sửa.");
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError) {
|
||||
options.setEntityStatus(`Mở section thất bại: ${err.body}`);
|
||||
} else {
|
||||
options.setEntityStatus("Mở section thất bại.");
|
||||
}
|
||||
} finally {
|
||||
options.setIsOpeningSection(false);
|
||||
}
|
||||
}, [openSectionForEditing, options]);
|
||||
|
||||
const createAndOpenSection = useCallback(async () => {
|
||||
const title = options.newSectionTitle.trim();
|
||||
if (!title) {
|
||||
options.setEntityStatus("Tên section là bắt buộc.");
|
||||
return;
|
||||
}
|
||||
if (options.pendingSaveCount > 0) {
|
||||
const confirmed = window.confirm("Section hiện tại có thay đổi chưa Commit. Tạo section mới sẽ bỏ các thay đổi này. Tiếp tục?");
|
||||
if (!confirmed) return;
|
||||
}
|
||||
|
||||
options.setIsOpeningSection(true);
|
||||
options.setEntityStatus(null);
|
||||
try {
|
||||
const section = await createSection({
|
||||
title,
|
||||
user_id: options.editorUserId,
|
||||
created_by: options.editorUserId,
|
||||
});
|
||||
const sections = await fetchSections();
|
||||
options.setAvailableSections(sections);
|
||||
options.setNewSectionTitle("");
|
||||
await openSectionForEditing(section.id);
|
||||
options.setEntityStatus("Đã tạo và mở section mới.");
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError) {
|
||||
options.setEntityStatus(`Tạo section thất bại: ${err.body}`);
|
||||
} else {
|
||||
options.setEntityStatus("Tạo section thất bại.");
|
||||
}
|
||||
} finally {
|
||||
options.setIsOpeningSection(false);
|
||||
}
|
||||
}, [openSectionForEditing, options]);
|
||||
|
||||
const submitCurrentSection = useCallback(async () => {
|
||||
if (!options.activeSection || !options.sectionState?.head_commit_id) {
|
||||
options.setEntityStatus("Section hiện tại chưa có head để submit.");
|
||||
return;
|
||||
}
|
||||
if (options.pendingSaveCount > 0) {
|
||||
options.setEntityStatus("Hãy Commit các thay đổi trước khi Submit.");
|
||||
return;
|
||||
}
|
||||
|
||||
options.setIsSubmitting(true);
|
||||
options.setEntityStatus(null);
|
||||
try {
|
||||
const submission = await submitSection(options.activeSection.id, {
|
||||
submitted_by: options.editorUserId,
|
||||
});
|
||||
options.setSectionState((prev) => prev ? { ...prev, status: "submitted" } : prev);
|
||||
options.setEntityStatus(`Đã submit section, submission ${submission.id}.`);
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError) {
|
||||
options.setEntityStatus(`Submit thất bại: ${err.body}`);
|
||||
} else {
|
||||
options.setEntityStatus("Submit thất bại.");
|
||||
}
|
||||
} finally {
|
||||
options.setIsSubmitting(false);
|
||||
}
|
||||
}, [options]);
|
||||
|
||||
const restoreCommit = useCallback(async (commitId: string) => {
|
||||
if (!options.activeSection || !options.sectionState) {
|
||||
options.setEntityStatus("Chưa mở được section editor.");
|
||||
return;
|
||||
}
|
||||
if (options.pendingSaveCount > 0) {
|
||||
options.setEntityStatus("Hãy Commit hoặc Undo thay đổi hiện tại trước khi Restore.");
|
||||
return;
|
||||
}
|
||||
|
||||
options.setIsSaving(true);
|
||||
options.setEntityStatus(null);
|
||||
try {
|
||||
const result = await restoreSectionCommit(options.activeSection.id, {
|
||||
commit_id: commitId,
|
||||
created_by: options.editorUserId,
|
||||
expected_version: options.sectionState.version,
|
||||
expected_head_commit_id: options.sectionState.head_commit_id,
|
||||
});
|
||||
const editorPayload = await openSectionEditor(options.activeSection.id, options.editorUserId);
|
||||
const snapshot = normalizeEditorSnapshot(editorPayload.snapshot);
|
||||
options.setSectionState(result.state);
|
||||
options.setLastSectionSnapshot(snapshot);
|
||||
if (snapshot?.editor_feature_collection) {
|
||||
options.setInitialData(snapshot.editor_feature_collection);
|
||||
}
|
||||
options.setSectionCommits(await fetchSectionCommits(options.activeSection.id));
|
||||
options.setEntityFormStatus(`Đã restore thành commit #${result.commit.commit_no}.`);
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError) {
|
||||
options.setEntityStatus(`Restore thất bại: ${err.body}`);
|
||||
} else {
|
||||
options.setEntityStatus("Restore thất bại.");
|
||||
}
|
||||
} finally {
|
||||
options.setIsSaving(false);
|
||||
}
|
||||
}, [options]);
|
||||
|
||||
return {
|
||||
openSectionForEditing,
|
||||
commitSection,
|
||||
openSelectedSection,
|
||||
createAndOpenSection,
|
||||
submitCurrentSection,
|
||||
restoreCommit,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user