feat: 멀티테넌시 지원을 위한 레이어 관리 기능 추가
- 레이어 목록 조회, 특정 레이어 레이아웃 조회, 레이어 삭제 및 조건 설정 업데이트 기능을 추가했습니다. - 엔티티 참조 데이터 조회 및 공통 코드 데이터 조회에 멀티테넌시 필터를 적용하여 인증된 사용자의 회사 코드에 따라 데이터 접근을 제한했습니다. - 레이어 관리 패널에서 기본 레이어와 조건부 레이어의 컴포넌트를 통합하여 조건부 영역의 표시를 개선했습니다. - 레이아웃 저장 시 레이어 ID를 포함하여 레이어별로 저장할 수 있도록 변경했습니다.
This commit is contained in:
@@ -38,7 +38,7 @@ export const API_BASE_URL = getApiBaseUrl();
|
||||
export const getFullImageUrl = (imagePath: string): string => {
|
||||
// 빈 값 체크
|
||||
if (!imagePath) return "";
|
||||
|
||||
|
||||
// 이미 전체 URL인 경우 그대로 반환
|
||||
if (imagePath.startsWith("http://") || imagePath.startsWith("https://")) {
|
||||
return imagePath;
|
||||
@@ -49,18 +49,18 @@ export const getFullImageUrl = (imagePath: string): string => {
|
||||
// 런타임에 현재 hostname 확인 (SSR 시점이 아닌 클라이언트에서 실행될 때)
|
||||
if (typeof window !== "undefined") {
|
||||
const currentHost = window.location.hostname;
|
||||
|
||||
|
||||
// 프로덕션 환경: v1.vexplor.com → api.vexplor.com
|
||||
if (currentHost === "v1.vexplor.com") {
|
||||
return `https://api.vexplor.com${imagePath}`;
|
||||
}
|
||||
|
||||
|
||||
// 로컬 개발환경
|
||||
if (currentHost === "localhost" || currentHost === "127.0.0.1") {
|
||||
return `http://localhost:8080${imagePath}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// SSR 또는 알 수 없는 환경에서는 API_BASE_URL 사용 (fallback)
|
||||
// 주의: 프로덕션 URL이 https://api.vexplor.com/api 이므로
|
||||
// 단순 .replace("/api", "")는 호스트명의 /api까지 제거하는 버그 발생
|
||||
@@ -69,7 +69,7 @@ export const getFullImageUrl = (imagePath: string): string => {
|
||||
if (baseUrl.startsWith("http://") || baseUrl.startsWith("https://")) {
|
||||
return `${baseUrl}${imagePath}`;
|
||||
}
|
||||
|
||||
|
||||
// 최종 fallback
|
||||
return imagePath;
|
||||
}
|
||||
@@ -157,7 +157,7 @@ const refreshToken = async (): Promise<string | null> => {
|
||||
headers: {
|
||||
Authorization: `Bearer ${currentToken}`,
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (response.data?.success && response.data?.data?.token) {
|
||||
@@ -192,13 +192,16 @@ const startAutoRefresh = (): void => {
|
||||
}
|
||||
|
||||
// 10분마다 토큰 상태 확인
|
||||
tokenRefreshTimer = setInterval(async () => {
|
||||
const token = TokenManager.getToken();
|
||||
if (token && TokenManager.isTokenExpiringSoon(token)) {
|
||||
console.log("[TokenManager] 토큰 만료 임박, 자동 갱신 시작...");
|
||||
await refreshToken();
|
||||
}
|
||||
}, 10 * 60 * 1000); // 10분
|
||||
tokenRefreshTimer = setInterval(
|
||||
async () => {
|
||||
const token = TokenManager.getToken();
|
||||
if (token && TokenManager.isTokenExpiringSoon(token)) {
|
||||
console.log("[TokenManager] 토큰 만료 임박, 자동 갱신 시작...");
|
||||
await refreshToken();
|
||||
}
|
||||
},
|
||||
10 * 60 * 1000,
|
||||
); // 10분
|
||||
|
||||
// 페이지 로드 시 즉시 확인
|
||||
const token = TokenManager.getToken();
|
||||
@@ -230,14 +233,18 @@ const setupActivityBasedRefresh = (): void => {
|
||||
["click", "keydown", "scroll", "mousemove"].forEach((event) => {
|
||||
// 너무 잦은 호출 방지를 위해 throttle 적용
|
||||
let throttleTimer: NodeJS.Timeout | null = null;
|
||||
window.addEventListener(event, () => {
|
||||
if (!throttleTimer) {
|
||||
throttleTimer = setTimeout(() => {
|
||||
handleActivity();
|
||||
throttleTimer = null;
|
||||
}, 1000); // 1초 throttle
|
||||
}
|
||||
}, { passive: true });
|
||||
window.addEventListener(
|
||||
event,
|
||||
() => {
|
||||
if (!throttleTimer) {
|
||||
throttleTimer = setTimeout(() => {
|
||||
handleActivity();
|
||||
throttleTimer = null;
|
||||
}, 1000); // 1초 throttle
|
||||
}
|
||||
},
|
||||
{ passive: true },
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -213,6 +213,28 @@ export const screenApi = {
|
||||
await apiClient.post(`/screen-management/screens/${screenId}/layout-v2`, layoutData);
|
||||
},
|
||||
|
||||
// 🆕 레이어 목록 조회
|
||||
getScreenLayers: async (screenId: number): Promise<any[]> => {
|
||||
const response = await apiClient.get(`/screen-management/screens/${screenId}/layers`);
|
||||
return response.data.data || [];
|
||||
},
|
||||
|
||||
// 🆕 특정 레이어 레이아웃 조회
|
||||
getLayerLayout: async (screenId: number, layerId: number): Promise<any> => {
|
||||
const response = await apiClient.get(`/screen-management/screens/${screenId}/layers/${layerId}/layout`);
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
// 🆕 레이어 삭제
|
||||
deleteLayer: async (screenId: number, layerId: number): Promise<void> => {
|
||||
await apiClient.delete(`/screen-management/screens/${screenId}/layers/${layerId}`);
|
||||
},
|
||||
|
||||
// 🆕 레이어 조건 설정 업데이트
|
||||
updateLayerCondition: async (screenId: number, layerId: number, conditionConfig: any, layerName?: string): Promise<void> => {
|
||||
await apiClient.put(`/screen-management/screens/${screenId}/layers/${layerId}/condition`, { conditionConfig, layerName });
|
||||
},
|
||||
|
||||
// 연결된 모달 화면 감지
|
||||
detectLinkedModals: async (
|
||||
screenId: number,
|
||||
|
||||
Reference in New Issue
Block a user