196 lines
5.9 KiB
TypeScript
196 lines
5.9 KiB
TypeScript
import { API_ENDPOINTS } from "@/api/config";
|
|
import { jsonRequestInit, requestJson } from "@/api/http";
|
|
|
|
export type SectionState = {
|
|
section_id?: string;
|
|
status: "editing" | "submitted" | "approved" | "rejected";
|
|
head_commit_id: string | null;
|
|
version: number;
|
|
locked_by: string | null;
|
|
locked_at: string | null;
|
|
lock_expires_at: string | null;
|
|
updated_at?: string;
|
|
};
|
|
|
|
export type Section = {
|
|
id: string;
|
|
title: string;
|
|
description: string | null;
|
|
user_id: string | null;
|
|
created_by: string | null;
|
|
created_at: string;
|
|
updated_at: string;
|
|
state: Omit<SectionState, "section_id" | "updated_at">;
|
|
};
|
|
|
|
export type SectionCommit = {
|
|
id: string;
|
|
section_id: string;
|
|
parent_commit_id: string | null;
|
|
commit_no: number;
|
|
kind: "manual" | "restore";
|
|
restored_from_commit_id: string | null;
|
|
created_by: string;
|
|
created_at: string;
|
|
title: string | null;
|
|
note: string | null;
|
|
snapshot_hash: string | null;
|
|
snapshot?: unknown;
|
|
};
|
|
|
|
export type SectionSubmission = {
|
|
id: string;
|
|
section_id: string;
|
|
commit_id: string;
|
|
submitted_by: string;
|
|
submitted_at: string;
|
|
status: "pending" | "approved" | "rejected" | "conflicted";
|
|
reviewed_by: string | null;
|
|
reviewed_at: string | null;
|
|
review_note: string | null;
|
|
snapshot_hash: string | null;
|
|
snapshot?: unknown;
|
|
};
|
|
|
|
export type EditorLoadResponse = {
|
|
section: Section;
|
|
state: SectionState;
|
|
commit: SectionCommit | null;
|
|
snapshot: unknown;
|
|
};
|
|
|
|
export type CreateSectionInput = {
|
|
id?: string;
|
|
title: string;
|
|
description?: string | null;
|
|
user_id?: string;
|
|
created_by?: string;
|
|
};
|
|
|
|
export type CreateCommitInput = {
|
|
snapshot: unknown;
|
|
created_by?: string;
|
|
user_id?: string;
|
|
expected_version?: number;
|
|
expected_head_commit_id?: string | null;
|
|
title?: string | null;
|
|
note?: string | null;
|
|
};
|
|
|
|
export async function fetchSections(): Promise<Section[]> {
|
|
return requestJson<Section[]>(API_ENDPOINTS.sections);
|
|
}
|
|
|
|
export async function createSection(input: CreateSectionInput): Promise<Section> {
|
|
return requestJson<Section>(API_ENDPOINTS.sections, jsonRequestInit("POST", input));
|
|
}
|
|
|
|
export async function openSectionEditor(sectionId: string, userId?: string): Promise<EditorLoadResponse> {
|
|
const params = new URLSearchParams();
|
|
if (userId) params.set("user_id", userId);
|
|
return requestJson<EditorLoadResponse>(sectionUrl(sectionId, "editor", params));
|
|
}
|
|
|
|
export async function lockSection(sectionId: string, userId: string): Promise<{ state: SectionState }> {
|
|
return requestJson<{ state: SectionState }>(
|
|
sectionUrl(sectionId, "lock"),
|
|
jsonRequestInit("POST", { user_id: userId })
|
|
);
|
|
}
|
|
|
|
export async function unlockSection(sectionId: string, userId: string): Promise<{ success: boolean }> {
|
|
return requestJson<{ success: boolean }>(
|
|
sectionUrl(sectionId, "unlock"),
|
|
jsonRequestInit("POST", { user_id: userId })
|
|
);
|
|
}
|
|
|
|
export async function createSectionCommit(
|
|
sectionId: string,
|
|
input: CreateCommitInput
|
|
): Promise<{ commit: SectionCommit; state: SectionState }> {
|
|
return requestJson<{ commit: SectionCommit; state: SectionState }>(
|
|
sectionUrl(sectionId, "commits"),
|
|
jsonRequestInit("POST", input)
|
|
);
|
|
}
|
|
|
|
export async function fetchSectionCommits(
|
|
sectionId: string,
|
|
options?: { includeSnapshot?: boolean }
|
|
): Promise<SectionCommit[]> {
|
|
const params = new URLSearchParams();
|
|
if (options?.includeSnapshot) params.set("include_snapshot", "1");
|
|
return requestJson<SectionCommit[]>(sectionUrl(sectionId, "commits", params));
|
|
}
|
|
|
|
export async function restoreSectionCommit(
|
|
sectionId: string,
|
|
input: {
|
|
commit_id: string;
|
|
created_by?: string;
|
|
user_id?: string;
|
|
expected_version?: number;
|
|
expected_head_commit_id?: string | null;
|
|
title?: string | null;
|
|
note?: string | null;
|
|
}
|
|
): Promise<{ commit: SectionCommit; state: SectionState }> {
|
|
return requestJson<{ commit: SectionCommit; state: SectionState }>(
|
|
sectionUrl(sectionId, "restore"),
|
|
jsonRequestInit("POST", input)
|
|
);
|
|
}
|
|
|
|
export async function submitSection(
|
|
sectionId: string,
|
|
input: { submitted_by?: string; user_id?: string }
|
|
): Promise<SectionSubmission> {
|
|
return requestJson<SectionSubmission>(sectionUrl(sectionId, "submit"), jsonRequestInit("POST", input));
|
|
}
|
|
|
|
export async function fetchSectionSubmissions(
|
|
sectionId: string,
|
|
options?: { includeSnapshot?: boolean }
|
|
): Promise<SectionSubmission[]> {
|
|
const params = new URLSearchParams();
|
|
if (options?.includeSnapshot) params.set("include_snapshot", "1");
|
|
return requestJson<SectionSubmission[]>(sectionUrl(sectionId, "submissions", params));
|
|
}
|
|
|
|
export async function approveSubmission(
|
|
submissionId: string,
|
|
input: { reviewed_by?: string; user_id?: string; review_note?: string | null }
|
|
): Promise<SectionSubmission> {
|
|
return requestJson<SectionSubmission>(
|
|
submissionUrl(submissionId, "approve"),
|
|
jsonRequestInit("POST", input)
|
|
);
|
|
}
|
|
|
|
export async function rejectSubmission(
|
|
submissionId: string,
|
|
input: { reviewed_by?: string; user_id?: string; review_note?: string | null }
|
|
): Promise<SectionSubmission> {
|
|
return requestJson<SectionSubmission>(
|
|
submissionUrl(submissionId, "reject"),
|
|
jsonRequestInit("POST", input)
|
|
);
|
|
}
|
|
|
|
function sectionUrl(sectionId: string, path?: string, params?: URLSearchParams): string {
|
|
return appendQuery(
|
|
`${API_ENDPOINTS.sections}/${encodeURIComponent(sectionId)}${path ? `/${path}` : ""}`,
|
|
params
|
|
);
|
|
}
|
|
|
|
function submissionUrl(submissionId: string, path: "approve" | "reject"): string {
|
|
return `${API_ENDPOINTS.submissions}/${encodeURIComponent(submissionId)}/${path}`;
|
|
}
|
|
|
|
function appendQuery(url: string, params?: URLSearchParams): string {
|
|
const suffix = params?.toString();
|
|
return suffix ? `${url}?${suffix}` : url;
|
|
}
|