327 lines
16 KiB
TypeScript
327 lines
16 KiB
TypeScript
"use client";
|
|
|
|
import React, { useMemo, useState, useEffect } from "react";
|
|
import dynamic from "next/dynamic";
|
|
import { Loader2 } from "lucide-react";
|
|
import { ScreenViewPageWrapper } from "@/app/(main)/screens/[screenId]/page";
|
|
import { apiClient } from "@/lib/api/client";
|
|
|
|
const LoadingFallback = () => (
|
|
<div className="flex h-full items-center justify-center">
|
|
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
|
|
</div>
|
|
);
|
|
|
|
function ScreenCodeResolver({ screenCode }: { screenCode: string }) {
|
|
const [screenId, setScreenId] = useState<number | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
const numericId = parseInt(screenCode);
|
|
if (!isNaN(numericId)) {
|
|
setScreenId(numericId);
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
const resolve = async () => {
|
|
try {
|
|
const res = await apiClient.get("/screen-management/screens", {
|
|
params: { searchTerm: screenCode, size: 50 },
|
|
});
|
|
const items = res.data?.data?.data || res.data?.data || [];
|
|
const arr = Array.isArray(items) ? items : [];
|
|
const exact = arr.find((s: any) => s.screenCode === screenCode);
|
|
const target = exact || arr[0];
|
|
if (target) setScreenId(target.screenId || target.screen_id);
|
|
} catch {
|
|
console.error("스크린 코드 변환 실패:", screenCode);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
resolve();
|
|
}, [screenCode]);
|
|
|
|
if (loading) return <LoadingFallback />;
|
|
if (!screenId) {
|
|
return (
|
|
<div className="flex h-full items-center justify-center">
|
|
<p className="text-sm text-muted-foreground">화면을 찾을 수 없습니다 (코드: {screenCode})</p>
|
|
</div>
|
|
);
|
|
}
|
|
return <ScreenViewPageWrapper screenIdProp={screenId} />;
|
|
}
|
|
|
|
const DashboardViewPage = dynamic(
|
|
() => import("@/app/(main)/dashboard/[dashboardId]/page"),
|
|
{ ssr: false, loading: LoadingFallback },
|
|
);
|
|
|
|
const ADMIN_PAGE_REGISTRY: Record<string, React.ComponentType<any>> = {
|
|
// 관리자 메인
|
|
"/admin": dynamic(() => import("@/app/(main)/admin/page"), { ssr: false, loading: LoadingFallback }),
|
|
|
|
// 메뉴 관리
|
|
"/admin/menu": dynamic(() => import("@/app/(main)/admin/menu/page"), { ssr: false, loading: LoadingFallback }),
|
|
|
|
// 사용자 관리
|
|
"/admin/userMng/userMngList": dynamic(() => import("@/app/(main)/admin/userMng/userMngList/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/userMng/rolesList": dynamic(() => import("@/app/(main)/admin/userMng/rolesList/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/userMng/userAuthList": dynamic(() => import("@/app/(main)/admin/userMng/userAuthList/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/userMng/companyList": dynamic(() => import("@/app/(main)/admin/userMng/companyList/page"), { ssr: false, loading: LoadingFallback }),
|
|
|
|
// 화면 관리
|
|
"/admin/screenMng/screenMngList": dynamic(() => import("@/app/(main)/admin/screenMng/screenMngList/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/screenMng/popScreenMngList": dynamic(() => import("@/app/(main)/admin/screenMng/popScreenMngList/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/screenMng/dashboardList": dynamic(() => import("@/app/(main)/admin/screenMng/dashboardList/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/screenMng/reportList": dynamic(() => import("@/app/(main)/admin/screenMng/reportList/page"), { ssr: false, loading: LoadingFallback }),
|
|
|
|
// 시스템 관리
|
|
"/admin/systemMng/commonCodeList": dynamic(() => import("@/app/(main)/admin/systemMng/commonCodeList/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/systemMng/tableMngList": dynamic(() => import("@/app/(main)/admin/systemMng/tableMngList/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/systemMng/i18nList": dynamic(() => import("@/app/(main)/admin/systemMng/i18nList/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/systemMng/collection-managementList": dynamic(() => import("@/app/(main)/admin/systemMng/collection-managementList/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/systemMng/cascading-managementList": dynamic(() => import("@/app/(main)/admin/cascading-management/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/systemMng/dataflow": dynamic(() => import("@/app/(main)/admin/systemMng/dataflow/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/systemMng/dataflow/node-editorList": dynamic(() => import("@/app/(main)/admin/systemMng/dataflow/page"), { ssr: false, loading: LoadingFallback }),
|
|
|
|
// 자동화 관리
|
|
"/admin/automaticMng/flowMgmtList": dynamic(() => import("@/app/(main)/admin/automaticMng/flowMgmtList/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/automaticMng/batchmngList": dynamic(() => import("@/app/(main)/admin/automaticMng/batchmngList/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/automaticMng/exconList": dynamic(() => import("@/app/(main)/admin/automaticMng/exconList/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/automaticMng/exCallConfList": dynamic(() => import("@/app/(main)/admin/automaticMng/exCallConfList/page"), { ssr: false, loading: LoadingFallback }),
|
|
|
|
// 메일
|
|
"/admin/automaticMng/mail/send": dynamic(() => import("@/app/(main)/admin/automaticMng/mail/send/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/automaticMng/mail/receive": dynamic(() => import("@/app/(main)/admin/automaticMng/mail/receive/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/automaticMng/mail/sent": dynamic(() => import("@/app/(main)/admin/automaticMng/mail/sent/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/automaticMng/mail/drafts": dynamic(() => import("@/app/(main)/admin/automaticMng/mail/drafts/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/automaticMng/mail/trash": dynamic(() => import("@/app/(main)/admin/automaticMng/mail/trash/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/automaticMng/mail/accounts": dynamic(() => import("@/app/(main)/admin/automaticMng/mail/accounts/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/automaticMng/mail/templates": dynamic(() => import("@/app/(main)/admin/automaticMng/mail/templates/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/automaticMng/mail/dashboardList": dynamic(() => import("@/app/(main)/admin/automaticMng/mail/dashboardList/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/automaticMng/mail/bulk-send": dynamic(() => import("@/app/(main)/admin/automaticMng/mail/bulk-send/page"), { ssr: false, loading: LoadingFallback }),
|
|
|
|
// 배치 관리
|
|
"/admin/batch-management": dynamic(() => import("@/app/(main)/admin/batch-management/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/batch-management-new": dynamic(() => import("@/app/(main)/admin/batch-management-new/page"), { ssr: false, loading: LoadingFallback }),
|
|
|
|
// 결재 관리
|
|
"/admin/approvalTemplate": dynamic(() => import("@/app/(main)/admin/approvalTemplate/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/approvalBox": dynamic(() => import("@/app/(main)/admin/approvalBox/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/approvalMng": dynamic(() => import("@/app/(main)/admin/approvalMng/page"), { ssr: false, loading: LoadingFallback }),
|
|
|
|
// 시스템
|
|
"/admin/audit-log": dynamic(() => import("@/app/(main)/admin/audit-log/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/system-notices": dynamic(() => import("@/app/(main)/admin/system-notices/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/aiAssistant": dynamic(() => import("@/app/(main)/admin/aiAssistant/page"), { ssr: false, loading: LoadingFallback }),
|
|
|
|
// 기타
|
|
"/admin/cascading-management": dynamic(() => import("@/app/(main)/admin/cascading-management/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/cascading-relations": dynamic(() => import("@/app/(main)/admin/cascading-management/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/layouts": dynamic(() => import("@/app/(main)/admin/layouts/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/templates": dynamic(() => import("@/app/(main)/admin/templates/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/monitoring": dynamic(() => import("@/app/(main)/admin/monitoring/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/standards": dynamic(() => import("@/app/(main)/admin/standards/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/flow-external-db": dynamic(() => import("@/app/(main)/admin/flow-external-db/page"), { ssr: false, loading: LoadingFallback }),
|
|
"/admin/auto-fill": dynamic(() => import("@/app/(main)/admin/cascading-management/page"), { ssr: false, loading: LoadingFallback }),
|
|
};
|
|
|
|
const DYNAMIC_ADMIN_IMPORTS: Record<string, () => Promise<any>> = {
|
|
"/admin/aiAssistant/dashboard": () => import("@/app/(main)/admin/aiAssistant/dashboard/page"),
|
|
"/admin/aiAssistant/history": () => import("@/app/(main)/admin/aiAssistant/history/page"),
|
|
"/admin/aiAssistant/api-keys": () => import("@/app/(main)/admin/aiAssistant/api-keys/page"),
|
|
"/admin/aiAssistant/api-test": () => import("@/app/(main)/admin/aiAssistant/api-test/page"),
|
|
"/admin/aiAssistant/usage": () => import("@/app/(main)/admin/aiAssistant/usage/page"),
|
|
"/admin/aiAssistant/chat": () => import("@/app/(main)/admin/aiAssistant/chat/page"),
|
|
"/admin/screenMng/barcodeList": () => import("@/app/(main)/admin/screenMng/barcodeList/page"),
|
|
"/admin/automaticMng/batchmngList/create": () => import("@/app/(main)/admin/automaticMng/batchmngList/create/page"),
|
|
"/admin/systemMng/dataflow/node-editorList": () => import("@/app/(main)/admin/systemMng/dataflow/page"),
|
|
"/admin/standards/new": () => import("@/app/(main)/admin/standards/new/page"),
|
|
};
|
|
|
|
const DYNAMIC_ADMIN_PATTERNS: Array<{
|
|
pattern: RegExp;
|
|
getImport: (match: RegExpMatchArray) => Promise<any>;
|
|
extractParams: (match: RegExpMatchArray) => Record<string, string>;
|
|
}> = [
|
|
{
|
|
pattern: /^\/admin\/userMng\/rolesList\/([^/]+)$/,
|
|
getImport: () => import("@/app/(main)/admin/userMng/rolesList/[id]/page"),
|
|
extractParams: (m) => ({ id: m[1] }),
|
|
},
|
|
{
|
|
pattern: /^\/admin\/screenMng\/dashboardList\/([^/]+)$/,
|
|
getImport: () => import("@/app/(main)/admin/screenMng/dashboardList/[id]/page"),
|
|
extractParams: (m) => ({ id: m[1] }),
|
|
},
|
|
{
|
|
pattern: /^\/admin\/automaticMng\/flowMgmtList\/([^/]+)$/,
|
|
getImport: () => import("@/app/(main)/admin/automaticMng/flowMgmtList/[id]/page"),
|
|
extractParams: (m) => ({ id: m[1] }),
|
|
},
|
|
{
|
|
pattern: /^\/admin\/automaticMng\/batchmngList\/edit\/([^/]+)$/,
|
|
getImport: () => import("@/app/(main)/admin/automaticMng/batchmngList/edit/[id]/page"),
|
|
extractParams: (m) => ({ id: m[1] }),
|
|
},
|
|
{
|
|
pattern: /^\/admin\/screenMng\/barcodeList\/designer\/([^/]+)$/,
|
|
getImport: () => import("@/app/(main)/admin/screenMng/barcodeList/designer/[labelId]/page"),
|
|
extractParams: (m) => ({ labelId: m[1] }),
|
|
},
|
|
{
|
|
pattern: /^\/admin\/screenMng\/reportList\/designer\/([^/]+)$/,
|
|
getImport: () => import("@/app/(main)/admin/screenMng/reportList/designer/[reportId]/page"),
|
|
extractParams: (m) => ({ reportId: m[1] }),
|
|
},
|
|
{
|
|
pattern: /^\/admin\/systemMng\/dataflow\/edit\/([^/]+)$/,
|
|
getImport: () => import("@/app/(main)/admin/systemMng/dataflow/edit/[diagramId]/page"),
|
|
extractParams: (m) => ({ diagramId: m[1] }),
|
|
},
|
|
{
|
|
pattern: /^\/admin\/userMng\/companyList\/([^/]+)\/departments$/,
|
|
getImport: () => import("@/app/(main)/admin/userMng/companyList/[companyCode]/departments/page"),
|
|
extractParams: (m) => ({ companyCode: m[1] }),
|
|
},
|
|
{
|
|
pattern: /^\/admin\/standards\/([^/]+)\/edit$/,
|
|
getImport: () => import("@/app/(main)/admin/standards/[webType]/edit/page"),
|
|
extractParams: (m) => ({ webType: m[1] }),
|
|
},
|
|
{
|
|
pattern: /^\/admin\/standards\/([^/]+)$/,
|
|
getImport: () => import("@/app/(main)/admin/standards/[webType]/page"),
|
|
extractParams: (m) => ({ webType: m[1] }),
|
|
},
|
|
];
|
|
|
|
function DynamicAdminLoader({ url, params }: { url: string; params?: Record<string, string> }) {
|
|
const [Component, setComponent] = useState<React.ComponentType<any> | null>(null);
|
|
const [failed, setFailed] = useState(false);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
|
|
const tryLoad = async () => {
|
|
// 1) 정적 import 목록
|
|
const staticImport = DYNAMIC_ADMIN_IMPORTS[url];
|
|
if (staticImport) {
|
|
try {
|
|
const mod = await staticImport();
|
|
if (!cancelled) setComponent(() => mod.default);
|
|
} catch {
|
|
if (!cancelled) setFailed(true);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// 2) 동적 라우트 패턴 매칭
|
|
for (const { pattern, getImport } of DYNAMIC_ADMIN_PATTERNS) {
|
|
const match = url.match(pattern);
|
|
if (match) {
|
|
try {
|
|
const mod = await getImport();
|
|
if (!cancelled) setComponent(() => mod.default);
|
|
} catch {
|
|
if (!cancelled) setFailed(true);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 3) URL 경로 기반 자동 import 시도
|
|
const pagePath = url.replace(/^\//, "");
|
|
try {
|
|
const mod = await import(
|
|
/* webpackMode: "lazy" */
|
|
/* webpackInclude: /\/page\.tsx$/ */
|
|
`@/app/(main)/${pagePath}/page`
|
|
);
|
|
if (!cancelled) setComponent(() => mod.default);
|
|
} catch {
|
|
console.warn("[DynamicAdminLoader] 자동 import 실패:", url);
|
|
if (!cancelled) setFailed(true);
|
|
}
|
|
};
|
|
|
|
tryLoad();
|
|
return () => { cancelled = true; };
|
|
}, [url]);
|
|
|
|
if (failed) return <AdminPageFallback url={url} />;
|
|
if (!Component) return <LoadingFallback />;
|
|
if (params) return <Component params={Promise.resolve(params)} />;
|
|
return <Component />;
|
|
}
|
|
|
|
function AdminPageFallback({ url }: { url: string }) {
|
|
return (
|
|
<div className="flex h-full items-center justify-center">
|
|
<div className="text-center">
|
|
<p className="text-lg font-semibold text-foreground">페이지 로딩 불가</p>
|
|
<p className="mt-1 text-sm text-muted-foreground">경로: {url}</p>
|
|
<p className="mt-2 text-xs text-muted-foreground">해당 페이지가 존재하지 않습니다.</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
interface AdminPageRendererProps {
|
|
url: string;
|
|
}
|
|
|
|
export function AdminPageRenderer({ url }: AdminPageRendererProps) {
|
|
const cleanUrl = url.split("?")[0].split("#")[0].replace(/\/$/, "");
|
|
|
|
console.log("[AdminPageRenderer] 렌더링:", { url, cleanUrl });
|
|
|
|
// 화면 할당: /screens/[id]
|
|
const screensIdMatch = cleanUrl.match(/^\/screens\/(\d+)$/);
|
|
if (screensIdMatch) {
|
|
console.log("[AdminPageRenderer] → /screens/[id] 매칭:", screensIdMatch[1]);
|
|
return <ScreenViewPageWrapper screenIdProp={parseInt(screensIdMatch[1])} />;
|
|
}
|
|
|
|
// 화면 할당: /screen/[code] (구 형식)
|
|
const screenCodeMatch = cleanUrl.match(/^\/screen\/([^/]+)$/);
|
|
if (screenCodeMatch) {
|
|
console.log("[AdminPageRenderer] → /screen/[code] 매칭:", screenCodeMatch[1]);
|
|
return <ScreenCodeResolver screenCode={screenCodeMatch[1]} />;
|
|
}
|
|
|
|
// 대시보드 할당: /dashboard/[id]
|
|
const dashboardMatch = cleanUrl.match(/^\/dashboard\/([^/]+)$/);
|
|
if (dashboardMatch) {
|
|
console.log("[AdminPageRenderer] → /dashboard/[id] 매칭:", dashboardMatch[1]);
|
|
return <DashboardViewPage params={Promise.resolve({ dashboardId: dashboardMatch[1] })} />;
|
|
}
|
|
|
|
// URL 직접 입력: 레지스트리 매칭
|
|
const PageComponent = useMemo(() => {
|
|
return ADMIN_PAGE_REGISTRY[cleanUrl] || null;
|
|
}, [cleanUrl]);
|
|
|
|
if (PageComponent) {
|
|
console.log("[AdminPageRenderer] → 레지스트리 매칭:", cleanUrl);
|
|
return <PageComponent />;
|
|
}
|
|
|
|
// 레지스트리에 없으면 동적 import 시도
|
|
// 동적 라우트 패턴 매칭 (params 추출)
|
|
for (const { pattern, extractParams } of DYNAMIC_ADMIN_PATTERNS) {
|
|
const match = cleanUrl.match(pattern);
|
|
if (match) {
|
|
const params = extractParams(match);
|
|
console.log("[AdminPageRenderer] → 동적 라우트 매칭:", cleanUrl, params);
|
|
return <DynamicAdminLoader url={cleanUrl} params={params} />;
|
|
}
|
|
}
|
|
|
|
// 레지스트리/패턴에 없으면 DynamicAdminLoader가 자동 import 시도
|
|
console.log("[AdminPageRenderer] → 자동 import 시도:", cleanUrl);
|
|
return <DynamicAdminLoader url={cleanUrl} />;
|
|
}
|