Merge remote-tracking branch 'origin/main' into ksh
This commit is contained in:
231
frontend/lib/api/cascadingAutoFill.ts
Normal file
231
frontend/lib/api/cascadingAutoFill.ts
Normal file
@@ -0,0 +1,231 @@
|
||||
/**
|
||||
* 자동 입력 (Auto-Fill) API 클라이언트
|
||||
*/
|
||||
|
||||
import { apiClient } from "./client";
|
||||
|
||||
// =====================================================
|
||||
// 타입 정의
|
||||
// =====================================================
|
||||
|
||||
export interface AutoFillMapping {
|
||||
mappingId?: number;
|
||||
sourceColumn: string;
|
||||
targetField: string;
|
||||
targetLabel?: string;
|
||||
isEditable?: string;
|
||||
isRequired?: string;
|
||||
defaultValue?: string;
|
||||
sortOrder?: number;
|
||||
}
|
||||
|
||||
export interface AutoFillGroup {
|
||||
groupId?: number;
|
||||
groupCode: string;
|
||||
groupName: string;
|
||||
description?: string;
|
||||
masterTable: string;
|
||||
masterValueColumn: string;
|
||||
masterLabelColumn?: string;
|
||||
companyCode?: string;
|
||||
isActive?: string;
|
||||
createdDate?: string;
|
||||
updatedDate?: string;
|
||||
mappingCount?: number;
|
||||
mappings?: AutoFillMapping[];
|
||||
}
|
||||
|
||||
export interface AutoFillOption {
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface AutoFillDataResponse {
|
||||
data: Record<string, any>;
|
||||
mappings: Array<{
|
||||
targetField: string;
|
||||
targetLabel: string;
|
||||
value: any;
|
||||
isEditable: boolean;
|
||||
isRequired: boolean;
|
||||
}>;
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// API 함수
|
||||
// =====================================================
|
||||
|
||||
/**
|
||||
* 자동 입력 그룹 목록 조회
|
||||
*/
|
||||
export async function getAutoFillGroups(isActive?: string): Promise<{
|
||||
success: boolean;
|
||||
data?: AutoFillGroup[];
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const params = new URLSearchParams();
|
||||
if (isActive) params.append("isActive", isActive);
|
||||
|
||||
const response = await apiClient.get(`/cascading-auto-fill/groups?${params.toString()}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("자동 입력 그룹 목록 조회 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 자동 입력 그룹 상세 조회 (매핑 포함)
|
||||
*/
|
||||
export async function getAutoFillGroupDetail(groupCode: string): Promise<{
|
||||
success: boolean;
|
||||
data?: AutoFillGroup;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.get(`/cascading-auto-fill/groups/${groupCode}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("자동 입력 그룹 상세 조회 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 자동 입력 그룹 생성
|
||||
*/
|
||||
export async function createAutoFillGroup(data: {
|
||||
groupCode: string;
|
||||
groupName: string;
|
||||
description?: string;
|
||||
masterTable: string;
|
||||
masterValueColumn: string;
|
||||
masterLabelColumn?: string;
|
||||
mappings?: AutoFillMapping[];
|
||||
}): Promise<{
|
||||
success: boolean;
|
||||
data?: AutoFillGroup;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.post("/cascading-auto-fill/groups", data);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("자동 입력 그룹 생성 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 자동 입력 그룹 수정
|
||||
*/
|
||||
export async function updateAutoFillGroup(
|
||||
groupCode: string,
|
||||
data: Partial<AutoFillGroup> & { mappings?: AutoFillMapping[] }
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
data?: AutoFillGroup;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.put(`/cascading-auto-fill/groups/${groupCode}`, data);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("자동 입력 그룹 수정 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 자동 입력 그룹 삭제
|
||||
*/
|
||||
export async function deleteAutoFillGroup(groupCode: string): Promise<{
|
||||
success: boolean;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.delete(`/cascading-auto-fill/groups/${groupCode}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("자동 입력 그룹 삭제 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 마스터 옵션 목록 조회
|
||||
*/
|
||||
export async function getAutoFillMasterOptions(groupCode: string): Promise<{
|
||||
success: boolean;
|
||||
data?: AutoFillOption[];
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.get(`/cascading-auto-fill/options/${groupCode}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("마스터 옵션 목록 조회 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 자동 입력 데이터 조회
|
||||
* 마스터 값 선택 시 자동으로 입력할 데이터 조회
|
||||
*/
|
||||
export async function getAutoFillData(
|
||||
groupCode: string,
|
||||
masterValue: string
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
data?: Record<string, any>;
|
||||
mappings?: AutoFillDataResponse["mappings"];
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.get(
|
||||
`/cascading-auto-fill/data/${groupCode}?masterValue=${encodeURIComponent(masterValue)}`
|
||||
);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("자동 입력 데이터 조회 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 편의를 위한 네임스페이스 export
|
||||
export const cascadingAutoFillApi = {
|
||||
getGroups: getAutoFillGroups,
|
||||
getGroupDetail: getAutoFillGroupDetail,
|
||||
createGroup: createAutoFillGroup,
|
||||
updateGroup: updateAutoFillGroup,
|
||||
deleteGroup: deleteAutoFillGroup,
|
||||
getMasterOptions: getAutoFillMasterOptions,
|
||||
getData: getAutoFillData,
|
||||
};
|
||||
|
||||
206
frontend/lib/api/cascadingCondition.ts
Normal file
206
frontend/lib/api/cascadingCondition.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
/**
|
||||
* 조건부 연쇄 (Conditional Cascading) API 클라이언트
|
||||
*/
|
||||
|
||||
import { apiClient } from "./client";
|
||||
|
||||
// =====================================================
|
||||
// 타입 정의
|
||||
// =====================================================
|
||||
|
||||
export interface CascadingCondition {
|
||||
conditionId?: number;
|
||||
relationType: string; // "RELATION" | "HIERARCHY"
|
||||
relationCode: string;
|
||||
conditionName: string;
|
||||
conditionField: string;
|
||||
conditionOperator: string; // "EQ" | "NEQ" | "CONTAINS" | "IN" | "GT" | "LT" 등
|
||||
conditionValue: string;
|
||||
filterColumn: string;
|
||||
filterValues: string; // 콤마로 구분된 값들
|
||||
priority?: number;
|
||||
companyCode?: string;
|
||||
isActive?: string;
|
||||
createdDate?: string;
|
||||
updatedDate?: string;
|
||||
}
|
||||
|
||||
// 연산자 목록
|
||||
export const CONDITION_OPERATORS = [
|
||||
{ value: "EQ", label: "같음 (=)" },
|
||||
{ value: "NEQ", label: "같지 않음 (!=)" },
|
||||
{ value: "CONTAINS", label: "포함" },
|
||||
{ value: "NOT_CONTAINS", label: "포함하지 않음" },
|
||||
{ value: "STARTS_WITH", label: "시작" },
|
||||
{ value: "ENDS_WITH", label: "끝" },
|
||||
{ value: "IN", label: "목록에 포함" },
|
||||
{ value: "NOT_IN", label: "목록에 미포함" },
|
||||
{ value: "GT", label: "보다 큼 (>)" },
|
||||
{ value: "GTE", label: "보다 크거나 같음 (>=)" },
|
||||
{ value: "LT", label: "보다 작음 (<)" },
|
||||
{ value: "LTE", label: "보다 작거나 같음 (<=)" },
|
||||
{ value: "IS_NULL", label: "비어있음" },
|
||||
{ value: "IS_NOT_NULL", label: "비어있지 않음" },
|
||||
];
|
||||
|
||||
// =====================================================
|
||||
// API 함수
|
||||
// =====================================================
|
||||
|
||||
/**
|
||||
* 조건부 연쇄 규칙 목록 조회
|
||||
*/
|
||||
export async function getConditions(params?: {
|
||||
isActive?: string;
|
||||
relationCode?: string;
|
||||
relationType?: string;
|
||||
}): Promise<{
|
||||
success: boolean;
|
||||
data?: CascadingCondition[];
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (params?.isActive) searchParams.append("isActive", params.isActive);
|
||||
if (params?.relationCode) searchParams.append("relationCode", params.relationCode);
|
||||
if (params?.relationType) searchParams.append("relationType", params.relationType);
|
||||
|
||||
const response = await apiClient.get(`/cascading-conditions?${searchParams.toString()}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("조건부 연쇄 규칙 목록 조회 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 조건부 연쇄 규칙 상세 조회
|
||||
*/
|
||||
export async function getConditionDetail(conditionId: number): Promise<{
|
||||
success: boolean;
|
||||
data?: CascadingCondition;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.get(`/cascading-conditions/${conditionId}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("조건부 연쇄 규칙 상세 조회 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 조건부 연쇄 규칙 생성
|
||||
*/
|
||||
export async function createCondition(data: Omit<CascadingCondition, "conditionId">): Promise<{
|
||||
success: boolean;
|
||||
data?: CascadingCondition;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.post("/cascading-conditions", data);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("조건부 연쇄 규칙 생성 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 조건부 연쇄 규칙 수정
|
||||
*/
|
||||
export async function updateCondition(
|
||||
conditionId: number,
|
||||
data: Partial<CascadingCondition>
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
data?: CascadingCondition;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.put(`/cascading-conditions/${conditionId}`, data);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("조건부 연쇄 규칙 수정 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 조건부 연쇄 규칙 삭제
|
||||
*/
|
||||
export async function deleteCondition(conditionId: number): Promise<{
|
||||
success: boolean;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.delete(`/cascading-conditions/${conditionId}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("조건부 연쇄 규칙 삭제 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 조건에 따른 필터링된 옵션 조회
|
||||
*/
|
||||
export async function getFilteredOptions(
|
||||
relationCode: string,
|
||||
params: {
|
||||
conditionFieldValue?: string;
|
||||
parentValue?: string;
|
||||
}
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
data?: Array<{ value: string; label: string }>;
|
||||
appliedCondition?: { conditionId: number; conditionName: string } | null;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (params.conditionFieldValue) searchParams.append("conditionFieldValue", params.conditionFieldValue);
|
||||
if (params.parentValue) searchParams.append("parentValue", params.parentValue);
|
||||
|
||||
const response = await apiClient.get(
|
||||
`/cascading-conditions/filtered-options/${relationCode}?${searchParams.toString()}`
|
||||
);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("조건부 필터링 옵션 조회 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 편의를 위한 네임스페이스 export
|
||||
export const cascadingConditionApi = {
|
||||
getList: getConditions,
|
||||
getDetail: getConditionDetail,
|
||||
create: createCondition,
|
||||
update: updateCondition,
|
||||
delete: deleteCondition,
|
||||
getFilteredOptions,
|
||||
};
|
||||
|
||||
317
frontend/lib/api/cascadingHierarchy.ts
Normal file
317
frontend/lib/api/cascadingHierarchy.ts
Normal file
@@ -0,0 +1,317 @@
|
||||
/**
|
||||
* 다단계 계층 (Hierarchy) API 클라이언트
|
||||
*/
|
||||
|
||||
import { apiClient } from "./client";
|
||||
|
||||
// =====================================================
|
||||
// 타입 정의
|
||||
// =====================================================
|
||||
|
||||
export interface HierarchyLevel {
|
||||
levelId?: number;
|
||||
groupCode: string;
|
||||
companyCode?: string;
|
||||
levelOrder: number;
|
||||
levelName: string;
|
||||
levelCode?: string;
|
||||
tableName: string;
|
||||
valueColumn: string;
|
||||
labelColumn: string;
|
||||
parentKeyColumn?: string;
|
||||
filterColumn?: string;
|
||||
filterValue?: string;
|
||||
orderColumn?: string;
|
||||
orderDirection?: string;
|
||||
placeholder?: string;
|
||||
isRequired?: string;
|
||||
isSearchable?: string;
|
||||
isActive?: string;
|
||||
createdDate?: string;
|
||||
updatedDate?: string;
|
||||
}
|
||||
|
||||
export interface HierarchyGroup {
|
||||
groupId?: number;
|
||||
groupCode: string;
|
||||
groupName: string;
|
||||
description?: string;
|
||||
hierarchyType: "MULTI_TABLE" | "SELF_REFERENCE" | "BOM" | "TREE";
|
||||
maxLevels?: number;
|
||||
isFixedLevels?: string;
|
||||
// Self-reference 설정
|
||||
selfRefTable?: string;
|
||||
selfRefIdColumn?: string;
|
||||
selfRefParentColumn?: string;
|
||||
selfRefValueColumn?: string;
|
||||
selfRefLabelColumn?: string;
|
||||
selfRefLevelColumn?: string;
|
||||
selfRefOrderColumn?: string;
|
||||
// BOM 설정
|
||||
bomTable?: string;
|
||||
bomParentColumn?: string;
|
||||
bomChildColumn?: string;
|
||||
bomItemTable?: string;
|
||||
bomItemIdColumn?: string;
|
||||
bomItemLabelColumn?: string;
|
||||
bomQtyColumn?: string;
|
||||
bomLevelColumn?: string;
|
||||
// 메시지
|
||||
emptyMessage?: string;
|
||||
noOptionsMessage?: string;
|
||||
loadingMessage?: string;
|
||||
// 메타
|
||||
companyCode?: string;
|
||||
isActive?: string;
|
||||
createdBy?: string;
|
||||
createdDate?: string;
|
||||
updatedBy?: string;
|
||||
updatedDate?: string;
|
||||
// 조회 시 포함
|
||||
levels?: HierarchyLevel[];
|
||||
levelCount?: number;
|
||||
}
|
||||
|
||||
// 계층 타입
|
||||
export const HIERARCHY_TYPES = [
|
||||
{ value: "MULTI_TABLE", label: "다중 테이블 (국가>도시>구)" },
|
||||
{ value: "SELF_REFERENCE", label: "자기 참조 (조직도)" },
|
||||
{ value: "BOM", label: "BOM (부품 구조)" },
|
||||
{ value: "TREE", label: "트리 (카테고리)" },
|
||||
];
|
||||
|
||||
// =====================================================
|
||||
// API 함수
|
||||
// =====================================================
|
||||
|
||||
/**
|
||||
* 계층 그룹 목록 조회
|
||||
*/
|
||||
export async function getHierarchyGroups(params?: {
|
||||
isActive?: string;
|
||||
hierarchyType?: string;
|
||||
}): Promise<{
|
||||
success: boolean;
|
||||
data?: HierarchyGroup[];
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (params?.isActive) searchParams.append("isActive", params.isActive);
|
||||
if (params?.hierarchyType) searchParams.append("hierarchyType", params.hierarchyType);
|
||||
|
||||
const response = await apiClient.get(`/cascading-hierarchy?${searchParams.toString()}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("계층 그룹 목록 조회 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 계층 그룹 상세 조회 (레벨 포함)
|
||||
*/
|
||||
export async function getHierarchyGroupDetail(groupCode: string): Promise<{
|
||||
success: boolean;
|
||||
data?: HierarchyGroup;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.get(`/cascading-hierarchy/${groupCode}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("계층 그룹 상세 조회 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 계층 그룹 생성
|
||||
*/
|
||||
export async function createHierarchyGroup(
|
||||
data: Omit<HierarchyGroup, "groupId"> & { levels?: Partial<HierarchyLevel>[] }
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
data?: HierarchyGroup;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.post("/cascading-hierarchy", data);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("계층 그룹 생성 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 계층 그룹 수정
|
||||
*/
|
||||
export async function updateHierarchyGroup(
|
||||
groupCode: string,
|
||||
data: Partial<HierarchyGroup>
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
data?: HierarchyGroup;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.put(`/cascading-hierarchy/${groupCode}`, data);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("계층 그룹 수정 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 계층 그룹 삭제
|
||||
*/
|
||||
export async function deleteHierarchyGroup(groupCode: string): Promise<{
|
||||
success: boolean;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.delete(`/cascading-hierarchy/${groupCode}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("계층 그룹 삭제 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 레벨 추가
|
||||
*/
|
||||
export async function addLevel(
|
||||
groupCode: string,
|
||||
data: Partial<HierarchyLevel>
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
data?: HierarchyLevel;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.post(`/cascading-hierarchy/${groupCode}/levels`, data);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("레벨 추가 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 레벨 수정
|
||||
*/
|
||||
export async function updateLevel(
|
||||
levelId: number,
|
||||
data: Partial<HierarchyLevel>
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
data?: HierarchyLevel;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.put(`/cascading-hierarchy/levels/${levelId}`, data);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("레벨 수정 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 레벨 삭제
|
||||
*/
|
||||
export async function deleteLevel(levelId: number): Promise<{
|
||||
success: boolean;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.delete(`/cascading-hierarchy/levels/${levelId}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("레벨 삭제 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 레벨의 옵션 조회
|
||||
*/
|
||||
export async function getLevelOptions(
|
||||
groupCode: string,
|
||||
levelOrder: number,
|
||||
parentValue?: string
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
data?: Array<{ value: string; label: string }>;
|
||||
levelInfo?: {
|
||||
levelId: number;
|
||||
levelName: string;
|
||||
placeholder: string;
|
||||
isRequired: string;
|
||||
isSearchable: string;
|
||||
};
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const params = new URLSearchParams();
|
||||
if (parentValue) params.append("parentValue", parentValue);
|
||||
|
||||
const response = await apiClient.get(
|
||||
`/cascading-hierarchy/${groupCode}/options/${levelOrder}?${params.toString()}`
|
||||
);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("레벨 옵션 조회 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 편의를 위한 네임스페이스 export
|
||||
export const hierarchyApi = {
|
||||
getGroups: getHierarchyGroups,
|
||||
getDetail: getHierarchyGroupDetail,
|
||||
createGroup: createHierarchyGroup,
|
||||
updateGroup: updateHierarchyGroup,
|
||||
deleteGroup: deleteHierarchyGroup,
|
||||
addLevel,
|
||||
updateLevel,
|
||||
deleteLevel,
|
||||
getLevelOptions,
|
||||
};
|
||||
|
||||
215
frontend/lib/api/cascadingMutualExclusion.ts
Normal file
215
frontend/lib/api/cascadingMutualExclusion.ts
Normal file
@@ -0,0 +1,215 @@
|
||||
/**
|
||||
* 상호 배제 (Mutual Exclusion) API 클라이언트
|
||||
*/
|
||||
|
||||
import { apiClient } from "./client";
|
||||
|
||||
// =====================================================
|
||||
// 타입 정의
|
||||
// =====================================================
|
||||
|
||||
export interface MutualExclusion {
|
||||
exclusionId?: number;
|
||||
exclusionCode: string;
|
||||
exclusionName: string;
|
||||
fieldNames: string; // 콤마로 구분된 필드명 (예: "source_warehouse,target_warehouse")
|
||||
sourceTable: string;
|
||||
valueColumn: string;
|
||||
labelColumn?: string;
|
||||
exclusionType?: string; // "SAME_VALUE"
|
||||
errorMessage?: string;
|
||||
companyCode?: string;
|
||||
isActive?: string;
|
||||
createdDate?: string;
|
||||
}
|
||||
|
||||
// 배제 타입 목록
|
||||
export const EXCLUSION_TYPES = [
|
||||
{ value: "SAME_VALUE", label: "동일 값 배제" },
|
||||
{ value: "RELATED", label: "관련 값 배제 (예정)" },
|
||||
];
|
||||
|
||||
// =====================================================
|
||||
// API 함수
|
||||
// =====================================================
|
||||
|
||||
/**
|
||||
* 상호 배제 규칙 목록 조회
|
||||
*/
|
||||
export async function getExclusions(isActive?: string): Promise<{
|
||||
success: boolean;
|
||||
data?: MutualExclusion[];
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const params = new URLSearchParams();
|
||||
if (isActive) params.append("isActive", isActive);
|
||||
|
||||
const response = await apiClient.get(`/cascading-exclusions?${params.toString()}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("상호 배제 규칙 목록 조회 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 상호 배제 규칙 상세 조회
|
||||
*/
|
||||
export async function getExclusionDetail(exclusionId: number): Promise<{
|
||||
success: boolean;
|
||||
data?: MutualExclusion;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.get(`/cascading-exclusions/${exclusionId}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("상호 배제 규칙 상세 조회 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 상호 배제 규칙 생성
|
||||
*/
|
||||
export async function createExclusion(data: Omit<MutualExclusion, "exclusionId">): Promise<{
|
||||
success: boolean;
|
||||
data?: MutualExclusion;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.post("/cascading-exclusions", data);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("상호 배제 규칙 생성 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 상호 배제 규칙 수정
|
||||
*/
|
||||
export async function updateExclusion(
|
||||
exclusionId: number,
|
||||
data: Partial<MutualExclusion>
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
data?: MutualExclusion;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.put(`/cascading-exclusions/${exclusionId}`, data);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("상호 배제 규칙 수정 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 상호 배제 규칙 삭제
|
||||
*/
|
||||
export async function deleteExclusion(exclusionId: number): Promise<{
|
||||
success: boolean;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.delete(`/cascading-exclusions/${exclusionId}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("상호 배제 규칙 삭제 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 상호 배제 검증
|
||||
*/
|
||||
export async function validateExclusion(
|
||||
exclusionCode: string,
|
||||
fieldValues: Record<string, string>
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
data?: {
|
||||
isValid: boolean;
|
||||
errorMessage: string | null;
|
||||
conflictingFields: string[];
|
||||
};
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const response = await apiClient.post(`/cascading-exclusions/validate/${exclusionCode}`, {
|
||||
fieldValues,
|
||||
});
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("상호 배제 검증 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 배제된 옵션 조회 (다른 필드에서 선택한 값 제외)
|
||||
*/
|
||||
export async function getExcludedOptions(
|
||||
exclusionCode: string,
|
||||
params: {
|
||||
currentField?: string;
|
||||
selectedValues?: string; // 콤마로 구분된 값들
|
||||
}
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
data?: Array<{ value: string; label: string }>;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (params.currentField) searchParams.append("currentField", params.currentField);
|
||||
if (params.selectedValues) searchParams.append("selectedValues", params.selectedValues);
|
||||
|
||||
const response = await apiClient.get(
|
||||
`/cascading-exclusions/options/${exclusionCode}?${searchParams.toString()}`
|
||||
);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("배제된 옵션 조회 실패:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 편의를 위한 네임스페이스 export
|
||||
export const mutualExclusionApi = {
|
||||
getList: getExclusions,
|
||||
getDetail: getExclusionDetail,
|
||||
create: createExclusion,
|
||||
update: updateExclusion,
|
||||
delete: deleteExclusion,
|
||||
validate: validateExclusion,
|
||||
getExcludedOptions,
|
||||
};
|
||||
|
||||
161
frontend/lib/api/cascadingRelation.ts
Normal file
161
frontend/lib/api/cascadingRelation.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
import { apiClient } from "./client";
|
||||
|
||||
export interface CascadingRelation {
|
||||
relation_id: number;
|
||||
relation_code: string;
|
||||
relation_name: string;
|
||||
description?: string;
|
||||
parent_table: string;
|
||||
parent_value_column: string;
|
||||
parent_label_column?: string;
|
||||
child_table: string;
|
||||
child_filter_column: string;
|
||||
child_value_column: string;
|
||||
child_label_column: string;
|
||||
child_order_column?: string;
|
||||
child_order_direction?: string;
|
||||
empty_parent_message?: string;
|
||||
no_options_message?: string;
|
||||
loading_message?: string;
|
||||
clear_on_parent_change?: string;
|
||||
company_code: string;
|
||||
is_active?: string;
|
||||
created_by?: string;
|
||||
created_date?: string;
|
||||
updated_by?: string;
|
||||
updated_date?: string;
|
||||
}
|
||||
|
||||
export interface CascadingRelationCreateInput {
|
||||
relationCode: string;
|
||||
relationName: string;
|
||||
description?: string;
|
||||
parentTable: string;
|
||||
parentValueColumn: string;
|
||||
parentLabelColumn?: string;
|
||||
childTable: string;
|
||||
childFilterColumn: string;
|
||||
childValueColumn: string;
|
||||
childLabelColumn: string;
|
||||
childOrderColumn?: string;
|
||||
childOrderDirection?: string;
|
||||
emptyParentMessage?: string;
|
||||
noOptionsMessage?: string;
|
||||
loadingMessage?: string;
|
||||
clearOnParentChange?: boolean;
|
||||
}
|
||||
|
||||
export interface CascadingRelationUpdateInput extends Partial<CascadingRelationCreateInput> {
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
export interface CascadingOption {
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 연쇄 관계 목록 조회
|
||||
*/
|
||||
export const getCascadingRelations = async (isActive?: string) => {
|
||||
try {
|
||||
const params = new URLSearchParams();
|
||||
if (isActive !== undefined) {
|
||||
params.append("isActive", isActive);
|
||||
}
|
||||
const response = await apiClient.get(`/cascading-relations?${params.toString()}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("연쇄 관계 목록 조회 실패:", error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 연쇄 관계 상세 조회 (ID)
|
||||
*/
|
||||
export const getCascadingRelationById = async (id: number) => {
|
||||
try {
|
||||
const response = await apiClient.get(`/cascading-relations/${id}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("연쇄 관계 상세 조회 실패:", error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 연쇄 관계 코드로 조회
|
||||
*/
|
||||
export const getCascadingRelationByCode = async (code: string) => {
|
||||
try {
|
||||
const response = await apiClient.get(`/cascading-relations/code/${code}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("연쇄 관계 코드 조회 실패:", error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 연쇄 관계로 자식 옵션 조회
|
||||
*/
|
||||
export const getCascadingOptions = async (code: string, parentValue: string): Promise<{ success: boolean; data?: CascadingOption[]; error?: string }> => {
|
||||
try {
|
||||
const response = await apiClient.get(`/cascading-relations/options/${code}?parentValue=${encodeURIComponent(parentValue)}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("연쇄 옵션 조회 실패:", error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 연쇄 관계 생성
|
||||
*/
|
||||
export const createCascadingRelation = async (data: CascadingRelationCreateInput) => {
|
||||
try {
|
||||
const response = await apiClient.post("/cascading-relations", data);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("연쇄 관계 생성 실패:", error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 연쇄 관계 수정
|
||||
*/
|
||||
export const updateCascadingRelation = async (id: number, data: CascadingRelationUpdateInput) => {
|
||||
try {
|
||||
const response = await apiClient.put(`/cascading-relations/${id}`, data);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("연쇄 관계 수정 실패:", error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 연쇄 관계 삭제
|
||||
*/
|
||||
export const deleteCascadingRelation = async (id: number) => {
|
||||
try {
|
||||
const response = await apiClient.delete(`/cascading-relations/${id}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("연쇄 관계 삭제 실패:", error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
};
|
||||
|
||||
export const cascadingRelationApi = {
|
||||
getList: getCascadingRelations,
|
||||
getById: getCascadingRelationById,
|
||||
getByCode: getCascadingRelationByCode,
|
||||
getOptions: getCascadingOptions,
|
||||
create: createCascadingRelation,
|
||||
update: updateCascadingRelation,
|
||||
delete: deleteCascadingRelation,
|
||||
};
|
||||
|
||||
@@ -525,3 +525,37 @@ export async function getFlowAuditLogs(flowId: number, limit: number = 100): Pro
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 플로우 스텝 데이터 수정 API
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* 플로우 스텝 데이터 업데이트 (인라인 편집)
|
||||
* @param flowId 플로우 정의 ID
|
||||
* @param stepId 스텝 ID
|
||||
* @param recordId 레코드의 primary key 값
|
||||
* @param updateData 업데이트할 데이터
|
||||
*/
|
||||
export async function updateFlowStepData(
|
||||
flowId: number,
|
||||
stepId: number,
|
||||
recordId: string | number,
|
||||
updateData: Record<string, any>,
|
||||
): Promise<ApiResponse<{ success: boolean }>> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/flow/${flowId}/step/${stepId}/data/${recordId}`, {
|
||||
method: "PUT",
|
||||
headers: getAuthHeaders(),
|
||||
credentials: "include",
|
||||
body: JSON.stringify(updateData),
|
||||
});
|
||||
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
return {
|
||||
success: false,
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user