refactor: migrate project data models and transition editor state management to the new project-based API architecture.

This commit is contained in:
taDuc
2026-05-12 05:18:54 +07:00
parent 8f911abe35
commit 8f6d848d55
15 changed files with 162 additions and 169 deletions
+21 -21
View File
@@ -14,7 +14,7 @@ import GeometryBindingPanel from "@/uhm/components/editor/GeometryBindingPanel";
import { Entity, fetchEntities, searchEntitiesByName } from "@/uhm/api/entities"; import { Entity, fetchEntities, searchEntitiesByName } from "@/uhm/api/entities";
import { ApiError } from "@/uhm/api/http"; import { ApiError } from "@/uhm/api/http";
import { fetchCurrentUser } from "@/uhm/api/auth"; import { fetchCurrentUser } from "@/uhm/api/auth";
import { SectionCommit } from "@/uhm/api/sections"; import { ProjectCommit } from "@/uhm/api/projects";
import { searchWikisByTitle, type Wiki } from "@/uhm/api/wikis"; import { searchWikisByTitle, type Wiki } from "@/uhm/api/wikis";
import { searchGeometriesByEntityName, type EntityGeometriesSearchItem, type EntityGeometrySearchGeo } from "@/uhm/api/geometries"; import { searchGeometriesByEntityName, type EntityGeometriesSearchItem, type EntityGeometrySearchGeo } from "@/uhm/api/geometries";
import type { EntitySnapshot } from "@/uhm/types/entities"; import type { EntitySnapshot } from "@/uhm/types/entities";
@@ -57,13 +57,13 @@ import {
loadBackgroundLayerVisibilityFromStorage, loadBackgroundLayerVisibilityFromStorage,
persistBackgroundLayerVisibility, persistBackgroundLayerVisibility,
} from "@/uhm/lib/editor/background/backgroundVisibilityStorage"; } from "@/uhm/lib/editor/background/backgroundVisibilityStorage";
import { useSectionCommands } from "@/uhm/lib/editor/section/useSectionCommands"; import { useProjectCommands } from "@/uhm/lib/editor/project/useProjectCommands";
import { EMPTY_FEATURE_COLLECTION } from "@/uhm/lib/map/geo/constants"; import { EMPTY_FEATURE_COLLECTION } from "@/uhm/lib/map/geo/constants";
import { FIXED_TIMELINE_RANGE, clampYearToFixedRange } from "@/uhm/lib/utils/timeline"; import { FIXED_TIMELINE_RANGE, clampYearToFixedRange } from "@/uhm/lib/utils/timeline";
import { useFeatureCommands } from "./featureCommands"; import { useFeatureCommands } from "./featureCommands";
import { deleteSubmission } from "@/uhm/api/sections"; import { deleteSubmission } from "@/uhm/api/projects";
import type { WikiSnapshot } from "@/uhm/types/wiki"; import type { WikiSnapshot } from "@/uhm/types/wiki";
import type { EntityWikiLinkSnapshot } from "@/uhm/types/sections"; import type { EntityWikiLinkSnapshot } from "@/uhm/types/projects";
import UnifiedSearchBar, { type UnifiedSearchKind } from "@/uhm/components/ui/UnifiedSearchBar"; import UnifiedSearchBar, { type UnifiedSearchKind } from "@/uhm/components/ui/UnifiedSearchBar";
const CURRENT_YEAR = new Date().getUTCFullYear(); const CURRENT_YEAR = new Date().getUTCFullYear();
@@ -107,8 +107,8 @@ export default function Page() {
isOpeningSection, isOpeningSection,
setIsOpeningSection, setIsOpeningSection,
setAvailableSections, setAvailableSections,
selectedSectionId, selectedProjectId,
setSelectedSectionId, setSelectedProjectId,
newSectionTitle, newSectionTitle,
setNewSectionTitle, setNewSectionTitle,
commitTitle, commitTitle,
@@ -116,10 +116,10 @@ export default function Page() {
editorUserIdInput, editorUserIdInput,
activeSection, activeSection,
setActiveSection, setActiveSection,
sectionState, projectState,
setSectionState, setProjectState,
sectionCommits, sectionCommits,
setSectionCommits, setProjectCommits,
baselineSnapshot, baselineSnapshot,
setBaselineSnapshot, setBaselineSnapshot,
entityCatalog, entityCatalog,
@@ -367,13 +367,13 @@ export default function Page() {
+ (entitiesDirty ? 1 : 0) + (entitiesDirty ? 1 : 0)
+ (entityWikiDirty ? 1 : 0); + (entityWikiDirty ? 1 : 0);
const sectionCommands = useSectionCommands({ const sectionCommands = useProjectCommands({
editor, editor,
editorUserId, editorUserId,
emptyFeatureCollection: EMPTY_FEATURE_COLLECTION, emptyFeatureCollection: EMPTY_FEATURE_COLLECTION,
activeSection, activeSection,
sectionState, projectState,
selectedSectionId, selectedProjectId,
newSectionTitle, newSectionTitle,
pendingSaveCount, pendingSaveCount,
snapshotEntities, snapshotEntities,
@@ -382,11 +382,11 @@ export default function Page() {
baselineSnapshot, baselineSnapshot,
commitTitle, commitTitle,
setActiveSection, setActiveSection,
setSelectedSectionId, setSelectedProjectId,
setSectionState, setProjectState,
setBaselineSnapshot, setBaselineSnapshot,
setInitialData, setInitialData,
setSectionCommits, setProjectCommits,
setSnapshotEntities, setSnapshotEntities,
setSnapshotWikis, setSnapshotWikis,
setSnapshotEntityWikiLinks, setSnapshotEntityWikiLinks,
@@ -1144,8 +1144,8 @@ export default function Page() {
} }
}; };
const headCommit = sectionState?.head_commit_id const headCommit = projectState?.head_commit_id
? sectionCommits.find((commit) => commit.id === sectionState.head_commit_id) || null ? sectionCommits.find((commit) => commit.id === projectState.head_commit_id) || null
: null; : null;
const handleCreateFeature = (feature: Feature) => { const handleCreateFeature = (feature: Feature) => {
@@ -1166,12 +1166,12 @@ export default function Page() {
isSaving={isSaving} isSaving={isSaving}
isSubmitting={isSubmitting} isSubmitting={isSubmitting}
sectionTitle={activeSection?.title || "Đang tải project"} sectionTitle={activeSection?.title || "Đang tải project"}
sectionStatus={sectionState?.status || "editing"} projectStatus={projectState?.status || "editing"}
commitTitle={commitTitle} commitTitle={commitTitle}
onCommitTitleChange={setCommitTitle} onCommitTitleChange={setCommitTitle}
commitCount={sectionCommits.length} commitCount={sectionCommits.length}
hasHeadCommit={Boolean(sectionState?.head_commit_id)} hasHeadCommit={Boolean(projectState?.head_commit_id)}
headCommitId={sectionState?.head_commit_id || null} headCommitId={projectState?.head_commit_id || null}
latestCommitLabel={headCommit ? `Head: ${formatCommitTitle(headCommit)}` : null} latestCommitLabel={headCommit ? `Head: ${formatCommitTitle(headCommit)}` : null}
commits={sectionCommits} commits={sectionCommits}
changesCount={pendingSaveCount} changesCount={pendingSaveCount}
@@ -1633,7 +1633,7 @@ function normalizeEditorUserId(value: string): string {
return normalized || DEFAULT_EDITOR_USER_ID; return normalized || DEFAULT_EDITOR_USER_ID;
} }
function formatCommitTitle(commit: SectionCommit): string { function formatCommitTitle(commit: ProjectCommit): string {
return commit.edit_summary?.trim() || `Commit ${commit.id.slice(0, 8)}`; return commit.edit_summary?.trim() || `Commit ${commit.id.slice(0, 8)}`;
} }
+1 -1
View File
@@ -14,7 +14,7 @@ import Badge from "@/components/ui/badge/Badge";
import { CreateProjectPayload, Project } from "@/interface/project"; import { CreateProjectPayload, Project } from "@/interface/project";
import { apiCreateProject, apiCreateProjectCommit, apiGetProjectCommits, getCurrentProject } from "@/service/projectService"; import { apiCreateProject, apiCreateProjectCommit, apiGetProjectCommits, getCurrentProject } from "@/service/projectService";
import { normalizeEditorSnapshot } from "@/uhm/lib/editor/snapshot/editorSnapshot"; import { normalizeEditorSnapshot } from "@/uhm/lib/editor/snapshot/editorSnapshot";
import type { EditorSnapshot } from "@/uhm/types/sections"; import type { EditorSnapshot } from "@/uhm/types/projects";
export type ProjectSortColumn = "created_at" | "updated_at" | "title"; export type ProjectSortColumn = "created_at" | "updated_at" | "title";
+1 -1
View File
@@ -209,7 +209,7 @@ export default function WikiEditorPage() {
type: "doc", type: "doc",
content: [ content: [
{ type: "paragraph", content: [{ type: "text", text: "Write your wiki content here." }] }, { type: "paragraph", content: [{ type: "text", text: "Write your wiki content here." }] },
{ type: "heading", attrs: { level: 2 }, content: [{ type: "text", text: "Section" }] }, { type: "heading", attrs: { level: 2 }, content: [{ type: "text", text: "Project" }] },
{ type: "paragraph", content: [{ type: "text", text: "Use H1/H2/H3 and the TOC will follow." }] }, { type: "paragraph", content: [{ type: "text", text: "Use H1/H2/H3 and the TOC will follow." }] },
], ],
}, },
@@ -2,43 +2,43 @@ import { API_BASE_URL, API_ENDPOINTS } from "@/uhm/api/config";
import { ApiError, jsonRequestInit, requestJson } from "@/uhm/api/http"; import { ApiError, jsonRequestInit, requestJson } from "@/uhm/api/http";
import type { import type {
CreateCommitInput, CreateCommitInput,
CreateSectionInput, CreateProjectInput,
EditorLoadResponse, EditorLoadResponse,
RestoreCommitInput, RestoreCommitInput,
Section, Project,
SectionCommit, ProjectCommit,
SectionState, ProjectState,
SectionSubmission, ProjectSubmission,
} from "@/uhm/types/sections"; } from "@/uhm/types/projects";
export type { export type {
CreateCommitInput, CreateCommitInput,
CreateSectionInput, CreateProjectInput,
EditorLoadResponse, EditorLoadResponse,
RestoreCommitInput, RestoreCommitInput,
Section, Project,
SectionCommit, ProjectCommit,
SectionState, ProjectState,
SectionSubmission, ProjectSubmission,
} from "@/uhm/types/sections"; } from "@/uhm/types/projects";
// Sections (API cũ) => Projects (API mới) // Projects (API cũ) => Projects (API mới)
export async function fetchSections(): Promise<Section[]> { export async function fetchProjects(): Promise<Project[]> {
// /users/current/project requires JWT. // /users/current/project requires JWT.
return requestJson<Section[]>(API_ENDPOINTS.currentUserProjects); return requestJson<Project[]>(API_ENDPOINTS.currentUserProjects);
} }
export async function createSection(input: CreateSectionInput): Promise<Section> { export async function createProject(input: CreateProjectInput): Promise<Project> {
// POST /projects // POST /projects
return requestJson<Section>(API_ENDPOINTS.projects, jsonRequestInit("POST", input)); return requestJson<Project>(API_ENDPOINTS.projects, jsonRequestInit("POST", input));
} }
export async function openSectionEditor(sectionId: string): Promise<EditorLoadResponse> { export async function openSectionEditor(projectId: string): Promise<EditorLoadResponse> {
// API mới không có endpoint "editor". FE tự load: // API mới không có endpoint "editor". FE tự load:
// 1) Project details // 1) Project details
// 2) Project commits (to get snapshot_json of latest commit) // 2) Project commits (to get snapshot_json of latest commit)
const project = await requestJson<Section>(`${API_ENDPOINTS.projects}/${encodeURIComponent(sectionId)}`); const project = await requestJson<Project>(`${API_ENDPOINTS.projects}/${encodeURIComponent(projectId)}`);
const pending = (project.submissions || []).find((s) => s?.status === "PENDING") || null; const pending = (project.submissions || []).find((s) => s?.status === "PENDING") || null;
if (pending) { if (pending) {
@@ -51,33 +51,33 @@ export async function openSectionEditor(sectionId: string): Promise<EditorLoadRe
); );
} }
const commits = await fetchSectionCommits(sectionId); const commits = await fetchProjectCommits(projectId);
const headCommitId = project.latest_commit_id ?? null; const headCommitId = project.latest_commit_id ?? null;
const headCommit = headCommitId ? commits.find((c) => c.id === headCommitId) || null : null; const headCommit = headCommitId ? commits.find((c) => c.id === headCommitId) || null : null;
const snapshot = headCommit?.snapshot_json ?? null; const snapshot = headCommit?.snapshot_json ?? null;
const state: SectionState = { const state: ProjectState = {
status: project.project_status || "ACTIVE", status: project.project_status || "ACTIVE",
head_commit_id: headCommitId, head_commit_id: headCommitId,
locked_by: project.locked_by ?? null, locked_by: project.locked_by ?? null,
}; };
return { return {
section: project, project: project,
state, state,
commit: headCommit, commit: headCommit,
snapshot, snapshot,
}; };
} }
export async function createSectionCommit( export async function createProjectCommit(
sectionId: string, projectId: string,
input: CreateCommitInput input: CreateCommitInput
): Promise<{ commit: SectionCommit; state: SectionState }> { ): Promise<{ commit: ProjectCommit; state: ProjectState }> {
// POST /projects/{id}/commits // POST /projects/{id}/commits
const commit = await requestJson<SectionCommit>( const commit = await requestJson<ProjectCommit>(
`${API_ENDPOINTS.projects}/${encodeURIComponent(sectionId)}/commits`, `${API_ENDPOINTS.projects}/${encodeURIComponent(projectId)}/commits`,
jsonRequestInit("POST", { jsonRequestInit("POST", {
snapshot_json: input.snapshot, snapshot_json: input.snapshot,
edit_summary: input.edit_summary, edit_summary: input.edit_summary,
@@ -85,8 +85,8 @@ export async function createSectionCommit(
); );
// Refresh project state (latest_commit_id may have moved). // Refresh project state (latest_commit_id may have moved).
const project = await requestJson<Section>(`${API_ENDPOINTS.projects}/${encodeURIComponent(sectionId)}`); const project = await requestJson<Project>(`${API_ENDPOINTS.projects}/${encodeURIComponent(projectId)}`);
const state: SectionState = { const state: ProjectState = {
status: project.project_status || "ACTIVE", status: project.project_status || "ACTIVE",
head_commit_id: project.latest_commit_id ?? null, head_commit_id: project.latest_commit_id ?? null,
locked_by: project.locked_by ?? null, locked_by: project.locked_by ?? null,
@@ -95,27 +95,27 @@ export async function createSectionCommit(
return { commit, state }; return { commit, state };
} }
export async function fetchSectionCommits(sectionId: string): Promise<SectionCommit[]> { export async function fetchProjectCommits(projectId: string): Promise<ProjectCommit[]> {
return requestJson<SectionCommit[]>(`${API_ENDPOINTS.projects}/${encodeURIComponent(sectionId)}/commits`); return requestJson<ProjectCommit[]>(`${API_ENDPOINTS.projects}/${encodeURIComponent(projectId)}/commits`);
} }
export async function restoreSectionCommit( export async function restoreProjectCommit(
sectionId: string, projectId: string,
input: RestoreCommitInput input: RestoreCommitInput
): Promise<{ commit: SectionCommit | null; state: SectionState }> { ): Promise<{ commit: ProjectCommit | null; state: ProjectState }> {
// POST /projects/{id}/commits/restore // POST /projects/{id}/commits/restore
await requestJson( await requestJson(
`${API_ENDPOINTS.projects}/${encodeURIComponent(sectionId)}/commits/restore`, `${API_ENDPOINTS.projects}/${encodeURIComponent(projectId)}/commits/restore`,
jsonRequestInit("POST", { commit_id: input.commit_id }) jsonRequestInit("POST", { commit_id: input.commit_id })
); );
// Reload commits + project to determine new head commit. // Reload commits + project to determine new head commit.
const project = await requestJson<Section>(`${API_ENDPOINTS.projects}/${encodeURIComponent(sectionId)}`); const project = await requestJson<Project>(`${API_ENDPOINTS.projects}/${encodeURIComponent(projectId)}`);
const commits = await fetchSectionCommits(sectionId); const commits = await fetchProjectCommits(projectId);
const headCommitId = project.latest_commit_id ?? null; const headCommitId = project.latest_commit_id ?? null;
const headCommit = headCommitId ? commits.find((c) => c.id === headCommitId) || null : null; const headCommit = headCommitId ? commits.find((c) => c.id === headCommitId) || null : null;
const state: SectionState = { const state: ProjectState = {
status: project.project_status || "ACTIVE", status: project.project_status || "ACTIVE",
head_commit_id: headCommitId, head_commit_id: headCommitId,
locked_by: project.locked_by ?? null, locked_by: project.locked_by ?? null,
@@ -124,18 +124,18 @@ export async function restoreSectionCommit(
return { commit: headCommit, state }; return { commit: headCommit, state };
} }
export async function submitSection(sectionId: string, content: string): Promise<SectionSubmission> { export async function submitSection(projectId: string, content: string): Promise<ProjectSubmission> {
// Submit latest commit of project // Submit latest commit of project
const project = await requestJson<Section>(`${API_ENDPOINTS.projects}/${encodeURIComponent(sectionId)}`); const project = await requestJson<Project>(`${API_ENDPOINTS.projects}/${encodeURIComponent(projectId)}`);
const commitId = project.latest_commit_id; const commitId = project.latest_commit_id;
if (!commitId) { if (!commitId) {
throw new Error("Project has no latest commit to submit"); throw new Error("Project has no latest commit to submit");
} }
return requestJson<SectionSubmission>( return requestJson<ProjectSubmission>(
API_ENDPOINTS.submissions, API_ENDPOINTS.submissions,
jsonRequestInit("POST", { jsonRequestInit("POST", {
project_id: sectionId, project_id: projectId,
commit_id: commitId, commit_id: commitId,
content: content, content: content,
}) })
+3 -3
View File
@@ -23,7 +23,7 @@ type Props = {
isSaving: boolean; isSaving: boolean;
isSubmitting: boolean; isSubmitting: boolean;
sectionTitle: string; sectionTitle: string;
sectionStatus: string; projectStatus: string;
commitTitle: string; commitTitle: string;
onCommitTitleChange: (title: string) => void; onCommitTitleChange: (title: string) => void;
commitCount: number; commitCount: number;
@@ -62,7 +62,7 @@ export default function Editor({
isSaving, isSaving,
isSubmitting, isSubmitting,
sectionTitle, sectionTitle,
sectionStatus, projectStatus,
commitTitle, commitTitle,
onCommitTitleChange, onCommitTitleChange,
commitCount, commitCount,
@@ -109,7 +109,7 @@ export default function Editor({
<ProjectPanel <ProjectPanel
sectionTitle={sectionTitle} sectionTitle={sectionTitle}
sectionStatus={sectionStatus} projectStatus={projectStatus}
commitCount={commitCount} commitCount={commitCount}
latestCommitLabel={latestCommitLabel} latestCommitLabel={latestCommitLabel}
/> />
@@ -3,7 +3,7 @@
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import type { Entity } from "@/uhm/types/entities"; import type { Entity } from "@/uhm/types/entities";
import type { WikiSnapshot } from "@/uhm/types/wiki"; import type { WikiSnapshot } from "@/uhm/types/wiki";
import type { EntityWikiLinkSnapshot } from "@/uhm/types/sections"; import type { EntityWikiLinkSnapshot } from "@/uhm/types/projects";
type EntityChoice = { id: string; name: string }; type EntityChoice = { id: string; name: string };
type WikiChoice = { id: string; title: string; operation?: string }; type WikiChoice = { id: string; title: string; operation?: string };
+3 -3
View File
@@ -2,14 +2,14 @@ import { Panel } from "./Panel";
type ProjectPanelProps = { type ProjectPanelProps = {
sectionTitle: string; sectionTitle: string;
sectionStatus: string; projectStatus: string;
commitCount: number; commitCount: number;
latestCommitLabel: string | null; latestCommitLabel: string | null;
}; };
export function ProjectPanel({ export function ProjectPanel({
sectionTitle, sectionTitle,
sectionStatus, projectStatus,
commitCount, commitCount,
latestCommitLabel, latestCommitLabel,
}: ProjectPanelProps) { }: ProjectPanelProps) {
@@ -18,7 +18,7 @@ export function ProjectPanel({
<div style={{ fontSize: 12, color: "#cbd5e1", lineHeight: 1.4 }}> <div style={{ fontSize: 12, color: "#cbd5e1", lineHeight: 1.4 }}>
<div style={{ color: "white", fontWeight: 850, overflowWrap: "anywhere" }}>{sectionTitle}</div> <div style={{ color: "white", fontWeight: 850, overflowWrap: "anywhere" }}>{sectionTitle}</div>
<div style={{ marginTop: 6 }}> <div style={{ marginTop: 6 }}>
Status: <span style={{ color: "#e2e8f0" }}>{sectionStatus}</span> Status: <span style={{ color: "#e2e8f0" }}>{projectStatus}</span>
</div> </div>
<div style={{ marginTop: 6 }}> <div style={{ marginTop: 6 }}>
Commits: <span style={{ color: "#e2e8f0" }}>{commitCount}</span> Commits: <span style={{ color: "#e2e8f0" }}>{commitCount}</span>
+1 -1
View File
@@ -6,7 +6,7 @@ import type {
} from "@/uhm/types/geo"; } from "@/uhm/types/geo";
import type { EntitySnapshot } from "@/uhm/types/entities"; import type { EntitySnapshot } from "@/uhm/types/entities";
import type { WikiSnapshot } from "@/uhm/types/wiki"; import type { WikiSnapshot } from "@/uhm/types/wiki";
import type { EntityWikiLinkSnapshot } from "@/uhm/types/sections"; import type { EntityWikiLinkSnapshot } from "@/uhm/types/projects";
export type Change = GeometryChange; export type Change = GeometryChange;
@@ -2,17 +2,17 @@ import { useCallback } from "react";
import type { Dispatch, SetStateAction } from "react"; import type { Dispatch, SetStateAction } from "react";
import { ApiError } from "@/uhm/api/http"; import { ApiError } from "@/uhm/api/http";
import { import {
createSection, createProject,
createSectionCommit, createProjectCommit,
fetchSectionCommits, fetchProjectCommits,
fetchSections, fetchProjects,
openSectionEditor, openSectionEditor,
submitSection, submitSection,
} from "@/uhm/api/sections"; } from "@/uhm/api/projects";
import { buildEditorSnapshot, normalizeEditorSnapshot } from "@/uhm/lib/editor/snapshot/editorSnapshot"; import { buildEditorSnapshot, normalizeEditorSnapshot } from "@/uhm/lib/editor/snapshot/editorSnapshot";
import type { Change } from "@/uhm/lib/editor/draft/editorTypes"; import type { Change } from "@/uhm/lib/editor/draft/editorTypes";
import type { Feature, FeatureCollection, FeatureId, GeometryEntitySnapshot, GeometrySnapshot } from "@/uhm/types/geo"; import type { Feature, FeatureCollection, FeatureId, GeometryEntitySnapshot, GeometrySnapshot } from "@/uhm/types/geo";
import type { EditorSnapshot, Section, SectionCommit, SectionState, EntityWikiLinkSnapshot } from "@/uhm/types/sections"; import type { EditorSnapshot, Project, ProjectCommit, ProjectState, EntityWikiLinkSnapshot } from "@/uhm/types/projects";
import type { EntitySnapshot } from "@/uhm/types/entities"; import type { EntitySnapshot } from "@/uhm/types/entities";
import type { WikiSnapshot } from "@/uhm/types/wiki"; import type { WikiSnapshot } from "@/uhm/types/wiki";
@@ -27,9 +27,9 @@ type Options = {
editor: EditorDraftApi; editor: EditorDraftApi;
editorUserId: string; editorUserId: string;
emptyFeatureCollection: FeatureCollection; emptyFeatureCollection: FeatureCollection;
activeSection: Section | null; activeSection: Project | null;
sectionState: SectionState | null; projectState: ProjectState | null;
selectedSectionId: string; selectedProjectId: string;
newSectionTitle: string; newSectionTitle: string;
pendingSaveCount: number; pendingSaveCount: number;
snapshotEntities: EntitySnapshot[]; snapshotEntities: EntitySnapshot[];
@@ -37,12 +37,12 @@ type Options = {
snapshotEntityWikiLinks: EntityWikiLinkSnapshot[]; snapshotEntityWikiLinks: EntityWikiLinkSnapshot[];
baselineSnapshot: EditorSnapshot | null; baselineSnapshot: EditorSnapshot | null;
commitTitle: string; commitTitle: string;
setActiveSection: Dispatch<SetStateAction<Section | null>>; setActiveSection: Dispatch<SetStateAction<Project | null>>;
setSelectedSectionId: Dispatch<SetStateAction<string>>; setSelectedProjectId: Dispatch<SetStateAction<string>>;
setSectionState: Dispatch<SetStateAction<SectionState | null>>; setProjectState: Dispatch<SetStateAction<ProjectState | null>>;
setBaselineSnapshot: Dispatch<SetStateAction<EditorSnapshot | null>>; setBaselineSnapshot: Dispatch<SetStateAction<EditorSnapshot | null>>;
setInitialData: Dispatch<SetStateAction<FeatureCollection>>; setInitialData: Dispatch<SetStateAction<FeatureCollection>>;
setSectionCommits: Dispatch<SetStateAction<SectionCommit[]>>; setProjectCommits: Dispatch<SetStateAction<ProjectCommit[]>>;
setSnapshotEntities: Dispatch<SetStateAction<EntitySnapshot[]>>; setSnapshotEntities: Dispatch<SetStateAction<EntitySnapshot[]>>;
setSnapshotWikis: Dispatch<SetStateAction<WikiSnapshot[]>>; setSnapshotWikis: Dispatch<SetStateAction<WikiSnapshot[]>>;
setSnapshotEntityWikiLinks: Dispatch<SetStateAction<EntityWikiLinkSnapshot[]>>; setSnapshotEntityWikiLinks: Dispatch<SetStateAction<EntityWikiLinkSnapshot[]>>;
@@ -52,27 +52,27 @@ type Options = {
setIsSaving: Dispatch<SetStateAction<boolean>>; setIsSaving: Dispatch<SetStateAction<boolean>>;
setIsSubmitting: Dispatch<SetStateAction<boolean>>; setIsSubmitting: Dispatch<SetStateAction<boolean>>;
setIsOpeningSection: Dispatch<SetStateAction<boolean>>; setIsOpeningSection: Dispatch<SetStateAction<boolean>>;
setAvailableSections: Dispatch<SetStateAction<Section[]>>; setAvailableSections: Dispatch<SetStateAction<Project[]>>;
setNewSectionTitle: Dispatch<SetStateAction<string>>; setNewSectionTitle: Dispatch<SetStateAction<string>>;
setCommitTitle: Dispatch<SetStateAction<string>>; setCommitTitle: Dispatch<SetStateAction<string>>;
}; };
export function useSectionCommands(options: Options) { export function useProjectCommands(options: Options) {
const openSectionForEditing = useCallback(async (sectionId: string) => { const openSectionForEditing = useCallback(async (projectId: string) => {
const editorPayload = await openSectionEditor(sectionId); const editorPayload = await openSectionEditor(projectId);
const snapshot = normalizeEditorSnapshot(editorPayload.snapshot); const snapshot = normalizeEditorSnapshot(editorPayload.snapshot);
// When starting a fresh editor session from a commit snapshot, treat all rows as baseline state: // When starting a fresh editor session from a commit snapshot, treat all rows as baseline state:
// operations should not carry over as deltas into the next commit. // operations should not carry over as deltas into the next commit.
const sessionSnapshot = snapshot ? toEditorSessionSnapshot(snapshot) : null; const sessionSnapshot = snapshot ? toEditorSessionSnapshot(snapshot) : null;
const commits = await fetchSectionCommits(sectionId); const commits = await fetchProjectCommits(projectId);
const nextInitialData = sessionSnapshot?.editor_feature_collection || options.emptyFeatureCollection; const nextInitialData = sessionSnapshot?.editor_feature_collection || options.emptyFeatureCollection;
options.setActiveSection(editorPayload.section); options.setActiveSection(editorPayload.project);
options.setSelectedSectionId(editorPayload.section.id); options.setSelectedProjectId(editorPayload.project.id);
options.setSectionState(editorPayload.state); options.setProjectState(editorPayload.state);
options.setBaselineSnapshot(sessionSnapshot); options.setBaselineSnapshot(sessionSnapshot);
options.setInitialData(nextInitialData); options.setInitialData(nextInitialData);
options.setSectionCommits(commits); options.setProjectCommits(commits);
options.setSnapshotEntities(sessionSnapshot?.entities || []); options.setSnapshotEntities(sessionSnapshot?.entities || []);
options.setSnapshotWikis(sessionSnapshot?.wikis || []); options.setSnapshotWikis(sessionSnapshot?.wikis || []);
options.setSnapshotEntityWikiLinks(sessionSnapshot?.entity_wiki || []); options.setSnapshotEntityWikiLinks(sessionSnapshot?.entity_wiki || []);
@@ -81,8 +81,8 @@ export function useSectionCommands(options: Options) {
}, [options]); }, [options]);
const commitSection = useCallback(async () => { const commitSection = useCallback(async () => {
if (!options.activeSection || !options.sectionState) { if (!options.activeSection || !options.projectState) {
options.setEntityStatus("Chưa mở được section editor."); options.setEntityStatus("Chưa mở được project editor.");
return; return;
} }
if (options.pendingSaveCount <= 0) { if (options.pendingSaveCount <= 0) {
@@ -95,7 +95,7 @@ export function useSectionCommands(options: Options) {
options.setEntityStatus(null); options.setEntityStatus(null);
try { try {
const snapshot = buildEditorSnapshot({ const snapshot = buildEditorSnapshot({
section: options.activeSection, project: options.activeSection,
draft: options.editor.draft, draft: options.editor.draft,
changes: geometryChanges, changes: geometryChanges,
snapshotEntities: options.snapshotEntities, snapshotEntities: options.snapshotEntities,
@@ -124,13 +124,13 @@ export function useSectionCommands(options: Options) {
// If stringify fails, let API call throw a more actionable error downstream. // If stringify fails, let API call throw a more actionable error downstream.
} }
const result = await createSectionCommit(options.activeSection.id, { const result = await createProjectCommit(options.activeSection.id, {
snapshot, snapshot,
edit_summary: editSummary, edit_summary: editSummary,
}); });
const sessionSnapshot = toEditorSessionSnapshot(snapshot); const sessionSnapshot = toEditorSessionSnapshot(snapshot);
options.setSectionState(result.state); options.setProjectState(result.state);
options.setBaselineSnapshot(sessionSnapshot); options.setBaselineSnapshot(sessionSnapshot);
options.setSnapshotEntities(sessionSnapshot.entities || []); options.setSnapshotEntities(sessionSnapshot.entities || []);
options.setSnapshotWikis(sessionSnapshot.wikis || []); options.setSnapshotWikis(sessionSnapshot.wikis || []);
@@ -138,7 +138,7 @@ export function useSectionCommands(options: Options) {
options.setInitialData(options.editor.draft); options.setInitialData(options.editor.draft);
options.editor.clearChanges(); options.editor.clearChanges();
options.setCommitTitle(""); options.setCommitTitle("");
options.setSectionCommits(await fetchSectionCommits(options.activeSection.id)); options.setProjectCommits(await fetchProjectCommits(options.activeSection.id));
options.setEntityFormStatus("Đã tạo commit."); options.setEntityFormStatus("Đã tạo commit.");
} catch (err) { } catch (err) {
if (err instanceof ApiError) { if (err instanceof ApiError) {
@@ -154,26 +154,26 @@ export function useSectionCommands(options: Options) {
}, [options]); }, [options]);
const openSelectedSection = useCallback(async () => { const openSelectedSection = useCallback(async () => {
const sectionId = options.selectedSectionId.trim(); const projectId = options.selectedProjectId.trim();
if (!sectionId) { if (!projectId) {
options.setEntityStatus("Hãy chọn section để mở."); options.setEntityStatus("Hãy chọn project để mở.");
return; return;
} }
if (options.pendingSaveCount > 0) { 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?"); const confirmed = window.confirm("Project hiện tại có thay đổi chưa Commit. Mở project khác sẽ bỏ các thay đổi này. Tiếp tục?");
if (!confirmed) return; if (!confirmed) return;
} }
options.setIsOpeningSection(true); options.setIsOpeningSection(true);
options.setEntityStatus(null); options.setEntityStatus(null);
try { try {
await openSectionForEditing(sectionId); await openSectionForEditing(projectId);
options.setEntityStatus("Đã mở section để chỉnh sửa."); options.setEntityStatus("Đã mở project để chỉnh sửa.");
} catch (err) { } catch (err) {
if (err instanceof ApiError) { if (err instanceof ApiError) {
options.setEntityStatus(`Mở section thất bại: ${err.body}`); options.setEntityStatus(`Mở project thất bại: ${err.body}`);
} else { } else {
options.setEntityStatus("Mở section thất bại."); options.setEntityStatus("Mở project thất bại.");
} }
} finally { } finally {
options.setIsOpeningSection(false); options.setIsOpeningSection(false);
@@ -183,31 +183,31 @@ export function useSectionCommands(options: Options) {
const createAndOpenSection = useCallback(async () => { const createAndOpenSection = useCallback(async () => {
const title = options.newSectionTitle.trim(); const title = options.newSectionTitle.trim();
if (!title) { if (!title) {
options.setEntityStatus("Tên section là bắt buộc."); options.setEntityStatus("Tên project là bắt buộc.");
return; return;
} }
if (options.pendingSaveCount > 0) { 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?"); const confirmed = window.confirm("Project hiện tại có thay đổi chưa Commit. Tạo project mới sẽ bỏ các thay đổi này. Tiếp tục?");
if (!confirmed) return; if (!confirmed) return;
} }
options.setIsOpeningSection(true); options.setIsOpeningSection(true);
options.setEntityStatus(null); options.setEntityStatus(null);
try { try {
const section = await createSection({ const project = await createProject({
title, title,
description: null, description: null,
}); });
const sections = await fetchSections(); const projects = await fetchProjects();
options.setAvailableSections(sections); options.setAvailableSections(projects);
options.setNewSectionTitle(""); options.setNewSectionTitle("");
await openSectionForEditing(section.id); await openSectionForEditing(project.id);
options.setEntityStatus("Đã tạo và mở section mới."); options.setEntityStatus("Đã tạo và mở project mới.");
} catch (err) { } catch (err) {
if (err instanceof ApiError) { if (err instanceof ApiError) {
options.setEntityStatus(`Tạo section thất bại: ${err.body}`); options.setEntityStatus(`Tạo project thất bại: ${err.body}`);
} else { } else {
options.setEntityStatus("Tạo section thất bại."); options.setEntityStatus("Tạo project thất bại.");
} }
} finally { } finally {
options.setIsOpeningSection(false); options.setIsOpeningSection(false);
@@ -215,8 +215,8 @@ export function useSectionCommands(options: Options) {
}, [openSectionForEditing, options]); }, [openSectionForEditing, options]);
const submitCurrentSection = useCallback(async (content: string) => { const submitCurrentSection = useCallback(async (content: string) => {
if (!options.activeSection || !options.sectionState?.head_commit_id) { if (!options.activeSection || !options.projectState?.head_commit_id) {
options.setEntityStatus("Section hiện tại chưa có head để submit."); options.setEntityStatus("Project hiện tại chưa có head để submit.");
return; return;
} }
if (options.pendingSaveCount > 0) { if (options.pendingSaveCount > 0) {
@@ -241,8 +241,8 @@ export function useSectionCommands(options: Options) {
}, [options]); }, [options]);
const restoreCommit = useCallback(async (commitId: string) => { const restoreCommit = useCallback(async (commitId: string) => {
if (!options.activeSection || !options.sectionState) { if (!options.activeSection || !options.projectState) {
options.setEntityStatus("Chưa mở được section editor."); options.setEntityStatus("Chưa mở được project editor.");
return; return;
} }
if (options.pendingSaveCount > 0) { if (options.pendingSaveCount > 0) {
@@ -255,8 +255,8 @@ export function useSectionCommands(options: Options) {
try { try {
// FE-only restore: load snapshot from selected commit and apply to editor state. // FE-only restore: load snapshot from selected commit and apply to editor state.
// Do NOT move project's head commit on backend. // Do NOT move project's head commit on backend.
const commits = await fetchSectionCommits(options.activeSection.id); const commits = await fetchProjectCommits(options.activeSection.id);
const target = commits.find((c: SectionCommit) => c.id === commitId) || null; const target = commits.find((c: ProjectCommit) => c.id === commitId) || null;
if (!target) { if (!target) {
options.setEntityStatus("Không tìm thấy commit để restore."); options.setEntityStatus("Không tìm thấy commit để restore.");
return; return;
@@ -274,8 +274,8 @@ export function useSectionCommands(options: Options) {
options.setSelectedFeatureIds([]); options.setSelectedFeatureIds([]);
options.setEntityFormStatus(null); options.setEntityFormStatus(null);
// Refresh commits list for UI, but keep sectionState/head as-is. // Refresh commits list for UI, but keep projectState/head as-is.
options.setSectionCommits(commits); options.setProjectCommits(commits);
options.setEntityFormStatus("Đã load snapshot từ commit (không đổi head trên BE)."); options.setEntityFormStatus("Đã load snapshot từ commit (không đổi head trên BE).");
} catch (err) { } catch (err) {
if (err instanceof ApiError) { if (err instanceof ApiError) {
@@ -1,15 +1,15 @@
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import type { Dispatch, SetStateAction } from "react"; import type { Dispatch, SetStateAction } from "react";
import type { EditorSnapshot, Section, SectionCommit, SectionState } from "@/uhm/types/sections"; import type { EditorSnapshot, Project, ProjectCommit, ProjectState } from "@/uhm/types/projects";
type Options = { type Options = {
defaultEditorUserId: string; defaultEditorUserId: string;
}; };
type SectionTask = "idle" | "saving" | "submitting" | "opening-section"; type SectionTask = "idle" | "saving" | "submitting" | "opening-project";
export function useSectionSessionState(options: Options) { export function useProjectSessionState(options: Options) {
// Single state machine cho các tác vụ async của section (saving/submitting/opening). // Single state machine cho các tác vụ async của project (saving/submitting/opening).
const [sectionTask, setSectionTask] = useState<SectionTask>("idle"); const [sectionTask, setSectionTask] = useState<SectionTask>("idle");
const setTaskFlag = useCallback((task: Exclude<SectionTask, "idle">, next: SetStateAction<boolean>) => { const setTaskFlag = useCallback((task: Exclude<SectionTask, "idle">, next: SetStateAction<boolean>) => {
setSectionTask((prev) => { setSectionTask((prev) => {
@@ -22,7 +22,7 @@ export function useSectionSessionState(options: Options) {
const isSaving = sectionTask === "saving"; const isSaving = sectionTask === "saving";
const isSubmitting = sectionTask === "submitting"; const isSubmitting = sectionTask === "submitting";
const isOpeningSection = sectionTask === "opening-section"; const isOpeningSection = sectionTask === "opening-project";
const setIsSaving: Dispatch<SetStateAction<boolean>> = useCallback((next) => { const setIsSaving: Dispatch<SetStateAction<boolean>> = useCallback((next) => {
setTaskFlag("saving", next); setTaskFlag("saving", next);
}, [setTaskFlag]); }, [setTaskFlag]);
@@ -30,25 +30,25 @@ export function useSectionSessionState(options: Options) {
setTaskFlag("submitting", next); setTaskFlag("submitting", next);
}, [setTaskFlag]); }, [setTaskFlag]);
const setIsOpeningSection: Dispatch<SetStateAction<boolean>> = useCallback((next) => { const setIsOpeningSection: Dispatch<SetStateAction<boolean>> = useCallback((next) => {
setTaskFlag("opening-section", next); setTaskFlag("opening-project", next);
}, [setTaskFlag]); }, [setTaskFlag]);
// Danh sách sections để user chọn mở. // Danh sách projects để user chọn mở.
const [availableSections, setAvailableSections] = useState<Section[]>([]); const [availableSections, setAvailableSections] = useState<Project[]>([]);
// Section ID đang được chọn trong dropdown. // Project ID đang được chọn trong dropdown.
const [selectedSectionId, setSelectedSectionId] = useState(""); const [selectedProjectId, setSelectedProjectId] = useState("");
// Title section mới (để create). // Title project mới (để create).
const [newSectionTitle, setNewSectionTitle] = useState(""); const [newSectionTitle, setNewSectionTitle] = useState("");
// Input title cho commit. // Input title cho commit.
const [commitTitle, setCommitTitle] = useState(""); const [commitTitle, setCommitTitle] = useState("");
// User ID dùng để gắn vào commit/submit/lock. // User ID dùng để gắn vào commit/submit/lock.
const [editorUserIdInput, setEditorUserIdInput] = useState(options.defaultEditorUserId); const [editorUserIdInput, setEditorUserIdInput] = useState(options.defaultEditorUserId);
// Section đang mở để edit (null nếu chưa mở). // Project đang mở để edit (null nếu chưa mở).
const [activeSection, setActiveSection] = useState<Section | null>(null); const [activeSection, setActiveSection] = useState<Project | null>(null);
// Trạng thái section (version/head/status/lock). // Trạng thái project (version/head/status/lock).
const [sectionState, setSectionState] = useState<SectionState | null>(null); const [projectState, setProjectState] = useState<ProjectState | null>(null);
// Danh sách commits của section đang mở. // Danh sách commits của project đang mở.
const [sectionCommits, setSectionCommits] = useState<SectionCommit[]>([]); const [sectionCommits, setProjectCommits] = useState<ProjectCommit[]>([]);
// Baseline snapshot currently loaded for this editor session. // Baseline snapshot currently loaded for this editor session.
const [baselineSnapshot, setBaselineSnapshot] = useState<EditorSnapshot | null>(null); const [baselineSnapshot, setBaselineSnapshot] = useState<EditorSnapshot | null>(null);
@@ -61,8 +61,8 @@ export function useSectionSessionState(options: Options) {
setIsOpeningSection, setIsOpeningSection,
availableSections, availableSections,
setAvailableSections, setAvailableSections,
selectedSectionId, selectedProjectId,
setSelectedSectionId, setSelectedProjectId,
newSectionTitle, newSectionTitle,
setNewSectionTitle, setNewSectionTitle,
commitTitle, commitTitle,
@@ -71,10 +71,10 @@ export function useSectionSessionState(options: Options) {
setEditorUserIdInput, setEditorUserIdInput,
activeSection, activeSection,
setActiveSection, setActiveSection,
sectionState, projectState,
setSectionState, setProjectState,
sectionCommits, sectionCommits,
setSectionCommits, setProjectCommits,
baselineSnapshot, baselineSnapshot,
setBaselineSnapshot, setBaselineSnapshot,
}; };
@@ -1,6 +1,6 @@
import { useState } from "react"; import { useState } from "react";
import type { WikiSnapshot } from "@/uhm/types/wiki"; import type { WikiSnapshot } from "@/uhm/types/wiki";
import type { EntityWikiLinkSnapshot } from "@/uhm/types/sections"; import type { EntityWikiLinkSnapshot } from "@/uhm/types/projects";
export function useWikiSessionState() { export function useWikiSessionState() {
const [snapshotWikis, setSnapshotWikis] = useState<WikiSnapshot[]>([]); const [snapshotWikis, setSnapshotWikis] = useState<WikiSnapshot[]>([]);
@@ -4,9 +4,9 @@ import type { Change } from "@/uhm/lib/editor/draft/editorTypes";
import type { EntitySnapshot } from "@/uhm/types/entities"; import type { EntitySnapshot } from "@/uhm/types/entities";
import type { EntitySnapshotOperation } from "@/uhm/types/entities"; import type { EntitySnapshotOperation } from "@/uhm/types/entities";
import type { Feature, FeatureCollection, GeometryEntitySnapshot, GeometrySnapshot } from "@/uhm/types/geo"; import type { Feature, FeatureCollection, GeometryEntitySnapshot, GeometrySnapshot } from "@/uhm/types/geo";
import type { EditorSnapshot, Section } from "@/uhm/types/sections"; import type { EditorSnapshot, Project } from "@/uhm/types/projects";
import type { WikiSnapshot } from "@/uhm/types/wiki"; import type { WikiSnapshot } from "@/uhm/types/wiki";
import type { EntityWikiLinkSnapshot } from "@/uhm/types/sections"; import type { EntityWikiLinkSnapshot } from "@/uhm/types/projects";
type UnknownRecord = Record<string, unknown>; type UnknownRecord = Record<string, unknown>;
@@ -251,7 +251,7 @@ export function normalizeEditorSnapshot(raw: unknown): EditorSnapshot | null {
} }
export function buildEditorSnapshot(options: { export function buildEditorSnapshot(options: {
section: Section; project: Project;
draft: FeatureCollection; draft: FeatureCollection;
changes: Change[]; changes: Change[];
snapshotEntities: EntitySnapshot[]; snapshotEntities: EntitySnapshot[];
@@ -2,7 +2,7 @@ import { useState } from "react";
import type { FeatureCollection } from "@/uhm/types/geo"; import type { FeatureCollection } from "@/uhm/types/geo";
import { useBackgroundSessionState } from "@/uhm/lib/editor/session/useBackgroundSessionState"; import { useBackgroundSessionState } from "@/uhm/lib/editor/session/useBackgroundSessionState";
import { useEntitySessionState } from "@/uhm/lib/editor/session/useEntitySessionState"; import { useEntitySessionState } from "@/uhm/lib/editor/session/useEntitySessionState";
import { useSectionSessionState } from "@/uhm/lib/editor/session/useSectionSessionState"; import { useProjectSessionState } from "@/uhm/lib/editor/session/useProjectSessionState";
import { useTimelineState } from "@/uhm/lib/editor/session/useTimelineState"; import { useTimelineState } from "@/uhm/lib/editor/session/useTimelineState";
import { useWikiSessionState } from "@/uhm/lib/editor/session/useWikiSessionState"; import { useWikiSessionState } from "@/uhm/lib/editor/session/useWikiSessionState";
import type { EditorMode, TimelineRange } from "@/uhm/lib/editor/session/sessionTypes"; import type { EditorMode, TimelineRange } from "@/uhm/lib/editor/session/sessionTypes";
@@ -24,10 +24,10 @@ type Options = {
export function useEditorSessionState(options: Options) { export function useEditorSessionState(options: Options) {
// Mode thao tác map/editor hiện tại. // Mode thao tác map/editor hiện tại.
const [mode, setMode] = useState<EditorMode>("idle"); const [mode, setMode] = useState<EditorMode>("idle");
// FeatureCollection "gốc" của session hiện tại (global timeline hoặc section snapshot). // FeatureCollection "gốc" của session hiện tại (global timeline hoặc project snapshot).
const [initialData, setInitialData] = useState<FeatureCollection>(options.emptyFeatureCollection); const [initialData, setInitialData] = useState<FeatureCollection>(options.emptyFeatureCollection);
const section = useSectionSessionState({ const project = useProjectSessionState({
defaultEditorUserId: options.defaultEditorUserId, defaultEditorUserId: options.defaultEditorUserId,
}); });
const entity = useEntitySessionState(); const entity = useEntitySessionState();
@@ -43,7 +43,7 @@ export function useEditorSessionState(options: Options) {
setMode, setMode,
initialData, initialData,
setInitialData, setInitialData,
...section, ...project,
...entity, ...entity,
...timeline, ...timeline,
...background, ...background,
+1 -1
View File
@@ -11,7 +11,7 @@ import { useUndoStack } from "@/uhm/lib/editor/draft/useUndoStack";
import type { Change, UndoAction } from "@/uhm/lib/editor/draft/editorTypes"; import type { Change, UndoAction } from "@/uhm/lib/editor/draft/editorTypes";
import type { EntitySnapshot } from "@/uhm/types/entities"; import type { EntitySnapshot } from "@/uhm/types/entities";
import type { WikiSnapshot } from "@/uhm/types/wiki"; import type { WikiSnapshot } from "@/uhm/types/wiki";
import type { EntityWikiLinkSnapshot } from "@/uhm/types/sections"; import type { EntityWikiLinkSnapshot } from "@/uhm/types/projects";
export type { Feature, FeatureCollection, FeatureProperties, Geometry } from "@/uhm/types/geo"; export type { Feature, FeatureCollection, FeatureProperties, Geometry } from "@/uhm/types/geo";
export type { Change, UndoAction } from "@/uhm/lib/editor/draft/editorTypes"; export type { Change, UndoAction } from "@/uhm/lib/editor/draft/editorTypes";
@@ -11,7 +11,7 @@ export type EntityWikiLinkSnapshot = {
operation?: "reference" | "binding" | "delete"; operation?: "reference" | "binding" | "delete";
}; };
// BackEndGo uses Projects/Commits/Submissions. "Section" is legacy naming in FE. // BackEndGo uses Projects/Commits/Submissions. "Project" is legacy naming in FE.
export type ProjectStatus = string; export type ProjectStatus = string;
export type ProjectSubmissionStatus = "PENDING" | "APPROVED" | "REJECTED" | string; export type ProjectSubmissionStatus = "PENDING" | "APPROVED" | "REJECTED" | string;
@@ -71,9 +71,9 @@ export type ProjectSubmission = {
}; };
export type EditorSnapshot = { export type EditorSnapshot = {
// Legacy: before BEGo flow moved fully to project/commit records, FE stored a minimal "section" ref // Legacy: before BEGo flow moved fully to project/commit records, FE stored a minimal "project" ref
// inside snapshot_json. New snapshots omit this entirely. // inside snapshot_json. New snapshots omit this entirely.
section?: { project?: {
id: string; id: string;
title: string; title: string;
}; };
@@ -90,13 +90,13 @@ export type EditorSnapshot = {
export type CommitSnapshot = EditorSnapshot; export type CommitSnapshot = EditorSnapshot;
export type EditorLoadResponse = { export type EditorLoadResponse = {
section: Project; project: Project;
state: ProjectState; state: ProjectState;
commit: ProjectCommit | null; commit: ProjectCommit | null;
snapshot: EditorSnapshot | null; snapshot: EditorSnapshot | null;
}; };
export type CreateSectionInput = { export type CreateProjectInput = {
title: string; title: string;
description?: string | null; description?: string | null;
status?: "PRIVATE" | "PUBLIC" | "ARCHIVE"; status?: "PRIVATE" | "PUBLIC" | "ARCHIVE";
@@ -111,10 +111,3 @@ export type RestoreCommitInput = {
commit_id: string; commit_id: string;
}; };
// Legacy aliases (to reduce churn in existing FE code). Prefer Project* names above.
export type SectionStatus = ProjectStatus;
export type SectionSubmissionStatus = ProjectSubmissionStatus;
export type SectionState = ProjectState;
export type Section = Project;
export type SectionCommit = ProjectCommit;
export type SectionSubmission = ProjectSubmission;