POP 전용 카테고리 트리 UI 구현 (계층적 폴더 구조) 카테고리 CRUD API 추가 (hierarchy_path LIKE 'POP/%' 필터) 화면 이동 기능 (기존 연결 삭제 후 새 연결 추가 방식) 카테고리/화면 순서 변경 기능 (display_order 교환) 이동 UI를 서브메뉴에서 검색 가능한 모달로 개선 POP 레이아웃 버전 통일 (pop-1.0) 및 로드 로직 수정 DB 스키마 호환성 수정 (writer 컬럼, is_active VARCHAR)
183 lines
5.6 KiB
TypeScript
183 lines
5.6 KiB
TypeScript
/**
|
|
* POP 화면 그룹 관리 API 클라이언트
|
|
* - hierarchy_path LIKE 'POP/%' 필터로 POP 카테고리만 조회
|
|
* - 데스크톱 screen_groups와 동일 테이블 사용, 필터로 분리
|
|
*/
|
|
|
|
import { apiClient } from "./client";
|
|
import { ScreenGroup, ScreenGroupScreen } from "./screenGroup";
|
|
|
|
// ============================================================
|
|
// POP 화면 그룹 타입 (ScreenGroup 재활용)
|
|
// ============================================================
|
|
|
|
export interface PopScreenGroup extends ScreenGroup {
|
|
// 추가 필드 필요시 여기에 정의
|
|
children?: PopScreenGroup[]; // 트리 구조용
|
|
}
|
|
|
|
export interface CreatePopScreenGroupRequest {
|
|
group_name: string;
|
|
group_code: string;
|
|
description?: string;
|
|
icon?: string;
|
|
display_order?: number;
|
|
parent_group_id?: number | null;
|
|
target_company_code?: string; // 최고관리자용
|
|
}
|
|
|
|
export interface UpdatePopScreenGroupRequest {
|
|
group_name?: string;
|
|
description?: string;
|
|
icon?: string;
|
|
display_order?: number;
|
|
is_active?: boolean;
|
|
}
|
|
|
|
// ============================================================
|
|
// API 함수
|
|
// ============================================================
|
|
|
|
/**
|
|
* POP 화면 그룹 목록 조회
|
|
* - hierarchy_path가 'POP'으로 시작하는 그룹만 조회
|
|
*/
|
|
export async function getPopScreenGroups(searchTerm?: string): Promise<PopScreenGroup[]> {
|
|
try {
|
|
const params = new URLSearchParams();
|
|
if (searchTerm) {
|
|
params.append("searchTerm", searchTerm);
|
|
}
|
|
|
|
const url = `/screen-groups/pop/groups${params.toString() ? `?${params.toString()}` : ""}`;
|
|
const response = await apiClient.get<{ success: boolean; data: PopScreenGroup[] }>(url);
|
|
|
|
if (response.data?.success) {
|
|
return response.data.data || [];
|
|
}
|
|
return [];
|
|
} catch (error) {
|
|
console.error("POP 화면 그룹 조회 실패:", error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* POP 화면 그룹 생성
|
|
*/
|
|
export async function createPopScreenGroup(
|
|
data: CreatePopScreenGroupRequest
|
|
): Promise<{ success: boolean; data?: PopScreenGroup; message?: string }> {
|
|
try {
|
|
const response = await apiClient.post<{ success: boolean; data: PopScreenGroup; message: string }>(
|
|
"/screen-groups/pop/groups",
|
|
data
|
|
);
|
|
return response.data;
|
|
} catch (error: any) {
|
|
console.error("POP 화면 그룹 생성 실패:", error);
|
|
return { success: false, message: error.response?.data?.message || "생성에 실패했습니다." };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* POP 화면 그룹 수정
|
|
*/
|
|
export async function updatePopScreenGroup(
|
|
id: number,
|
|
data: UpdatePopScreenGroupRequest
|
|
): Promise<{ success: boolean; data?: PopScreenGroup; message?: string }> {
|
|
try {
|
|
const response = await apiClient.put<{ success: boolean; data: PopScreenGroup; message: string }>(
|
|
`/screen-groups/pop/groups/${id}`,
|
|
data
|
|
);
|
|
return response.data;
|
|
} catch (error: any) {
|
|
console.error("POP 화면 그룹 수정 실패:", error);
|
|
return { success: false, message: error.response?.data?.message || "수정에 실패했습니다." };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* POP 화면 그룹 삭제
|
|
*/
|
|
export async function deletePopScreenGroup(
|
|
id: number
|
|
): Promise<{ success: boolean; message?: string }> {
|
|
try {
|
|
const response = await apiClient.delete<{ success: boolean; message: string }>(
|
|
`/screen-groups/pop/groups/${id}`
|
|
);
|
|
return response.data;
|
|
} catch (error: any) {
|
|
console.error("POP 화면 그룹 삭제 실패:", error);
|
|
return { success: false, message: error.response?.data?.message || "삭제에 실패했습니다." };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* POP 루트 그룹 확보 (없으면 자동 생성)
|
|
*/
|
|
export async function ensurePopRootGroup(): Promise<{ success: boolean; data?: PopScreenGroup; message?: string }> {
|
|
try {
|
|
const response = await apiClient.post<{ success: boolean; data: PopScreenGroup; message: string }>(
|
|
"/screen-groups/pop/ensure-root"
|
|
);
|
|
return response.data;
|
|
} catch (error: any) {
|
|
console.error("POP 루트 그룹 확보 실패:", error);
|
|
return { success: false, message: error.response?.data?.message || "루트 그룹 확보에 실패했습니다." };
|
|
}
|
|
}
|
|
|
|
// ============================================================
|
|
// 유틸리티 함수
|
|
// ============================================================
|
|
|
|
/**
|
|
* 플랫 리스트를 트리 구조로 변환
|
|
*/
|
|
export function buildPopGroupTree(groups: PopScreenGroup[]): PopScreenGroup[] {
|
|
const groupMap = new Map<number, PopScreenGroup>();
|
|
const rootGroups: PopScreenGroup[] = [];
|
|
|
|
// 먼저 모든 그룹을 맵에 저장
|
|
groups.forEach((group) => {
|
|
groupMap.set(group.id, { ...group, children: [] });
|
|
});
|
|
|
|
// 트리 구조 생성
|
|
groups.forEach((group) => {
|
|
const node = groupMap.get(group.id)!;
|
|
|
|
if (group.parent_group_id && groupMap.has(group.parent_group_id)) {
|
|
// 부모가 있으면 부모의 children에 추가
|
|
const parent = groupMap.get(group.parent_group_id)!;
|
|
parent.children = parent.children || [];
|
|
parent.children.push(node);
|
|
} else {
|
|
// 부모가 없거나 POP 루트면 최상위에 추가
|
|
// hierarchy_path가 'POP'이거나 'POP/XXX' 형태인지 확인
|
|
if (group.hierarchy_path === "POP" ||
|
|
(group.hierarchy_path?.startsWith("POP/") &&
|
|
group.hierarchy_path.split("/").length === 2)) {
|
|
rootGroups.push(node);
|
|
}
|
|
}
|
|
});
|
|
|
|
// display_order로 정렬
|
|
const sortByOrder = (a: PopScreenGroup, b: PopScreenGroup) =>
|
|
(a.display_order || 0) - (b.display_order || 0);
|
|
|
|
rootGroups.sort(sortByOrder);
|
|
rootGroups.forEach((group) => {
|
|
if (group.children) {
|
|
group.children.sort(sortByOrder);
|
|
}
|
|
});
|
|
|
|
return rootGroups;
|
|
}
|