Files
wace_rps/frontend/components/layout/AdminPageRenderer.tsx
T
kjs f32861df8b feat: add new design management pages and session files
- Introduced multiple new pages for design management, including change management, design requests, my work, project management, and task management.
- Added session files to track design sessions with relevant details such as session ID, end time, and reason.
- Enhanced the overall structure and organization of the design management features, improving user experience and functionality.

This commit expands the design management capabilities within the application, allowing for better tracking and handling of design-related tasks.
2026-03-27 14:48:15 +09:00

358 lines
17 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";
import { useAuth } from "@/hooks/useAuth";
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/crawlingList": dynamic(() => import("@/app/(main)/admin/automaticMng/crawlingList/page"), { ssr: false, loading: LoadingFallback }),
// 회사별 커스텀 페이지는 레지스트리에서 제거 — 회사코드 prefix로 자동 import 처리
// (design, sales, production, logistics, equipment, outsourcing, master-data)
"/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;
}
// 회사별 커스텀 페이지 경로 prefix 목록
// 이 prefix로 시작하는 URL은 회사코드 폴더에서 로드
const COMPANY_PAGE_PREFIXES = [
"/sales/",
"/master-data/",
"/production/",
"/equipment/",
"/logistics/",
"/outsourcing/",
"/design/",
];
function isCompanyPage(url: string): boolean {
return COMPANY_PAGE_PREFIXES.some((prefix) => url.startsWith(prefix));
}
export function AdminPageRenderer({ url }: AdminPageRendererProps) {
const { user } = useAuth();
const companyCode = user?.companyCode || user?.company_code;
const cleanUrl = url.split("?")[0].split("#")[0].replace(/\/$/, "");
// 회사별 커스텀 페이지: companyCode를 prefix로 붙여 경로 변환
// 예: /sales/order → /COMPANY_7/sales/order
const resolvedUrl = (companyCode && isCompanyPage(cleanUrl))
? `/${companyCode}${cleanUrl}`
: cleanUrl;
console.log("[AdminPageRenderer] 렌더링:", { url, cleanUrl, resolvedUrl, companyCode });
// 화면 할당: /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 직접 입력: 레지스트리 매칭 (admin 페이지 등 공통 페이지)
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 시도
// 회사별 페이지는 resolvedUrl로 import (예: COMPANY_7/sales/order/page)
console.log("[AdminPageRenderer] → 자동 import 시도:", resolvedUrl);
return <DynamicAdminLoader url={resolvedUrl} />;
}