[agent-pipeline] pipe-20260303124213-d7zo round-2
This commit is contained in:
450
frontend/lib/api/approval.ts
Normal file
450
frontend/lib/api/approval.ts
Normal file
@@ -0,0 +1,450 @@
|
||||
/**
|
||||
* 결재 시스템 API 클라이언트
|
||||
* 엔드포인트: /api/approval/*
|
||||
*/
|
||||
|
||||
// API URL 동적 설정
|
||||
const getApiBaseUrl = (): string => {
|
||||
if (process.env.NEXT_PUBLIC_API_URL) {
|
||||
return process.env.NEXT_PUBLIC_API_URL;
|
||||
}
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
const currentHost = window.location.hostname;
|
||||
|
||||
if (currentHost === "v1.vexplor.com") {
|
||||
return "https://api.vexplor.com/api";
|
||||
}
|
||||
|
||||
if (currentHost === "localhost" || currentHost === "127.0.0.1") {
|
||||
return "http://localhost:8080/api";
|
||||
}
|
||||
}
|
||||
|
||||
return "/api";
|
||||
};
|
||||
|
||||
const API_BASE = getApiBaseUrl();
|
||||
|
||||
function getAuthToken(): string | null {
|
||||
if (typeof window === "undefined") return null;
|
||||
return localStorage.getItem("authToken") || sessionStorage.getItem("authToken");
|
||||
}
|
||||
|
||||
function getAuthHeaders(): HeadersInit {
|
||||
const token = getAuthToken();
|
||||
const headers: HeadersInit = { "Content-Type": "application/json" };
|
||||
if (token) {
|
||||
(headers as Record<string, string>)["Authorization"] = `Bearer ${token}`;
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 공통 타입 정의
|
||||
// ============================================================
|
||||
|
||||
export interface ApiResponse<T = any> {
|
||||
success: boolean;
|
||||
data?: T;
|
||||
message?: string;
|
||||
error?: string;
|
||||
total?: number;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export interface ApprovalDefinition {
|
||||
definition_id: number;
|
||||
definition_name: string;
|
||||
definition_name_eng?: string;
|
||||
description?: string;
|
||||
default_template_id?: number;
|
||||
max_steps: number;
|
||||
allow_self_approval: boolean;
|
||||
allow_cancel: boolean;
|
||||
is_active: string;
|
||||
company_code: string;
|
||||
created_by?: string;
|
||||
created_at: string;
|
||||
updated_by?: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface ApprovalLineTemplate {
|
||||
template_id: number;
|
||||
template_name: string;
|
||||
description?: string;
|
||||
definition_id?: number;
|
||||
definition_name?: string;
|
||||
is_active: string;
|
||||
company_code: string;
|
||||
created_by?: string;
|
||||
created_at: string;
|
||||
updated_by?: string;
|
||||
updated_at: string;
|
||||
steps?: ApprovalLineTemplateStep[];
|
||||
}
|
||||
|
||||
export interface ApprovalLineTemplateStep {
|
||||
step_id: number;
|
||||
template_id: number;
|
||||
step_order: number;
|
||||
approver_type: "user" | "position" | "dept";
|
||||
approver_user_id?: string;
|
||||
approver_position?: string;
|
||||
approver_dept_code?: string;
|
||||
approver_label?: string;
|
||||
company_code: string;
|
||||
}
|
||||
|
||||
export interface ApprovalRequest {
|
||||
request_id: number;
|
||||
title: string;
|
||||
description?: string;
|
||||
definition_id?: number;
|
||||
definition_name?: string;
|
||||
target_table: string;
|
||||
target_record_id: string;
|
||||
target_record_data?: Record<string, any>;
|
||||
status: "requested" | "in_progress" | "approved" | "rejected" | "cancelled";
|
||||
current_step: number;
|
||||
total_steps: number;
|
||||
requester_id: string;
|
||||
requester_name?: string;
|
||||
requester_dept?: string;
|
||||
completed_at?: string;
|
||||
final_approver_id?: string;
|
||||
final_comment?: string;
|
||||
screen_id?: number;
|
||||
button_component_id?: string;
|
||||
company_code: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
lines?: ApprovalLine[];
|
||||
}
|
||||
|
||||
export interface ApprovalLine {
|
||||
line_id: number;
|
||||
request_id: number;
|
||||
step_order: number;
|
||||
approver_id: string;
|
||||
approver_name?: string;
|
||||
approver_position?: string;
|
||||
approver_dept?: string;
|
||||
approver_label?: string;
|
||||
status: "waiting" | "pending" | "approved" | "rejected" | "skipped";
|
||||
comment?: string;
|
||||
processed_at?: string;
|
||||
company_code: string;
|
||||
created_at: string;
|
||||
// 요청 정보 (my-pending 조회 시 포함)
|
||||
title?: string;
|
||||
target_table?: string;
|
||||
target_record_id?: string;
|
||||
requester_name?: string;
|
||||
requester_dept?: string;
|
||||
request_created_at?: string;
|
||||
}
|
||||
|
||||
export interface CreateApprovalRequestInput {
|
||||
title: string;
|
||||
description?: string;
|
||||
definition_id?: number;
|
||||
target_table: string;
|
||||
target_record_id: string;
|
||||
target_record_data?: Record<string, any>;
|
||||
screen_id?: number;
|
||||
button_component_id?: string;
|
||||
approvers: {
|
||||
approver_id: string;
|
||||
approver_name?: string;
|
||||
approver_position?: string;
|
||||
approver_dept?: string;
|
||||
approver_label?: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 결재 유형 (Definitions) API
|
||||
// ============================================================
|
||||
|
||||
export async function getApprovalDefinitions(params?: {
|
||||
is_active?: string;
|
||||
search?: string;
|
||||
}): Promise<ApiResponse<ApprovalDefinition[]>> {
|
||||
try {
|
||||
const qs = new URLSearchParams();
|
||||
if (params?.is_active) qs.append("is_active", params.is_active);
|
||||
if (params?.search) qs.append("search", params.search);
|
||||
|
||||
const response = await fetch(
|
||||
`${API_BASE}/approval/definitions${qs.toString() ? `?${qs}` : ""}`,
|
||||
{ headers: getAuthHeaders(), credentials: "include" }
|
||||
);
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
export async function getApprovalDefinition(id: number): Promise<ApiResponse<ApprovalDefinition>> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/approval/definitions/${id}`, {
|
||||
headers: getAuthHeaders(),
|
||||
credentials: "include",
|
||||
});
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
export async function createApprovalDefinition(data: {
|
||||
definition_name: string;
|
||||
definition_name_eng?: string;
|
||||
description?: string;
|
||||
default_template_id?: number;
|
||||
max_steps?: number;
|
||||
allow_self_approval?: boolean;
|
||||
allow_cancel?: boolean;
|
||||
is_active?: string;
|
||||
}): Promise<ApiResponse<ApprovalDefinition>> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/approval/definitions`, {
|
||||
method: "POST",
|
||||
headers: getAuthHeaders(),
|
||||
credentials: "include",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateApprovalDefinition(
|
||||
id: number,
|
||||
data: Partial<ApprovalDefinition>
|
||||
): Promise<ApiResponse<ApprovalDefinition>> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/approval/definitions/${id}`, {
|
||||
method: "PUT",
|
||||
headers: getAuthHeaders(),
|
||||
credentials: "include",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteApprovalDefinition(id: number): Promise<ApiResponse<void>> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/approval/definitions/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: getAuthHeaders(),
|
||||
credentials: "include",
|
||||
});
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 결재선 템플릿 (Templates) API
|
||||
// ============================================================
|
||||
|
||||
export async function getApprovalTemplates(params?: {
|
||||
definition_id?: number;
|
||||
is_active?: string;
|
||||
}): Promise<ApiResponse<ApprovalLineTemplate[]>> {
|
||||
try {
|
||||
const qs = new URLSearchParams();
|
||||
if (params?.definition_id) qs.append("definition_id", String(params.definition_id));
|
||||
if (params?.is_active) qs.append("is_active", params.is_active);
|
||||
|
||||
const response = await fetch(
|
||||
`${API_BASE}/approval/templates${qs.toString() ? `?${qs}` : ""}`,
|
||||
{ headers: getAuthHeaders(), credentials: "include" }
|
||||
);
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
export async function getApprovalTemplate(id: number): Promise<ApiResponse<ApprovalLineTemplate>> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/approval/templates/${id}`, {
|
||||
headers: getAuthHeaders(),
|
||||
credentials: "include",
|
||||
});
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
export async function createApprovalTemplate(data: {
|
||||
template_name: string;
|
||||
description?: string;
|
||||
definition_id?: number;
|
||||
is_active?: string;
|
||||
steps?: Omit<ApprovalLineTemplateStep, "step_id" | "template_id" | "company_code">[];
|
||||
}): Promise<ApiResponse<ApprovalLineTemplate>> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/approval/templates`, {
|
||||
method: "POST",
|
||||
headers: getAuthHeaders(),
|
||||
credentials: "include",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateApprovalTemplate(
|
||||
id: number,
|
||||
data: {
|
||||
template_name?: string;
|
||||
description?: string;
|
||||
definition_id?: number;
|
||||
is_active?: string;
|
||||
steps?: Omit<ApprovalLineTemplateStep, "step_id" | "template_id" | "company_code">[];
|
||||
}
|
||||
): Promise<ApiResponse<ApprovalLineTemplate>> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/approval/templates/${id}`, {
|
||||
method: "PUT",
|
||||
headers: getAuthHeaders(),
|
||||
credentials: "include",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteApprovalTemplate(id: number): Promise<ApiResponse<void>> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/approval/templates/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: getAuthHeaders(),
|
||||
credentials: "include",
|
||||
});
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 결재 요청 (Requests) API
|
||||
// ============================================================
|
||||
|
||||
export async function getApprovalRequests(params?: {
|
||||
status?: string;
|
||||
target_table?: string;
|
||||
requester_id?: string;
|
||||
my_approvals?: boolean;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}): Promise<ApiResponse<ApprovalRequest[]>> {
|
||||
try {
|
||||
const qs = new URLSearchParams();
|
||||
if (params?.status) qs.append("status", params.status);
|
||||
if (params?.target_table) qs.append("target_table", params.target_table);
|
||||
if (params?.requester_id) qs.append("requester_id", params.requester_id);
|
||||
if (params?.my_approvals !== undefined) qs.append("my_approvals", String(params.my_approvals));
|
||||
if (params?.page) qs.append("page", String(params.page));
|
||||
if (params?.limit) qs.append("limit", String(params.limit));
|
||||
|
||||
const response = await fetch(
|
||||
`${API_BASE}/approval/requests${qs.toString() ? `?${qs}` : ""}`,
|
||||
{ headers: getAuthHeaders(), credentials: "include" }
|
||||
);
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
export async function getApprovalRequest(id: number): Promise<ApiResponse<ApprovalRequest>> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/approval/requests/${id}`, {
|
||||
headers: getAuthHeaders(),
|
||||
credentials: "include",
|
||||
});
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
export async function createApprovalRequest(
|
||||
data: CreateApprovalRequestInput
|
||||
): Promise<ApiResponse<ApprovalRequest>> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/approval/requests`, {
|
||||
method: "POST",
|
||||
headers: getAuthHeaders(),
|
||||
credentials: "include",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
export async function cancelApprovalRequest(id: number): Promise<ApiResponse<void>> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/approval/requests/${id}/cancel`, {
|
||||
method: "POST",
|
||||
headers: getAuthHeaders(),
|
||||
credentials: "include",
|
||||
});
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 결재 라인 처리 (Lines) API
|
||||
// ============================================================
|
||||
|
||||
export async function getMyPendingApprovals(): Promise<ApiResponse<ApprovalLine[]>> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/approval/my-pending`, {
|
||||
headers: getAuthHeaders(),
|
||||
credentials: "include",
|
||||
});
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
export async function processApprovalLine(
|
||||
lineId: number,
|
||||
data: { action: "approved" | "rejected"; comment?: string }
|
||||
): Promise<ApiResponse<void>> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/approval/lines/${lineId}/process`, {
|
||||
method: "POST",
|
||||
headers: getAuthHeaders(),
|
||||
credentials: "include",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user