Files
vexplor/frontend/lib/api/popScreenGroup.ts
SeongHyun Kim d9b7ef9ad4 feat(pop): POP 화면 카테고리 관리 시스템 구현 및 저장/로드 안정화
POP 전용 카테고리 트리 UI 구현 (계층적 폴더 구조)
카테고리 CRUD API 추가 (hierarchy_path LIKE 'POP/%' 필터)
화면 이동 기능 (기존 연결 삭제 후 새 연결 추가 방식)
카테고리/화면 순서 변경 기능 (display_order 교환)
이동 UI를 서브메뉴에서 검색 가능한 모달로 개선
POP 레이아웃 버전 통일 (pop-1.0) 및 로드 로직 수정
DB 스키마 호환성 수정 (writer 컬럼, is_active VARCHAR)
2026-02-02 18:01:05 +09:00

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;
}