Merge branch 'ycshin-node' of http://39.117.244.52:3000/kjs/ERP-node into jskim-node
This commit is contained in:
@@ -29,17 +29,24 @@ import { evaluateConditional } from "@/lib/utils/conditionalEvaluator"; // 조
|
||||
import { ScreenMultiLangProvider } from "@/contexts/ScreenMultiLangContext"; // 화면 다국어
|
||||
import { convertV2ToLegacy, isValidV2Layout } from "@/lib/utils/layoutV2Converter"; // V2 Zod 기반 변환
|
||||
import { useScheduleGenerator, ScheduleConfirmDialog } from "@/lib/v2-core/services/ScheduleGeneratorService"; // 스케줄 자동 생성
|
||||
import { useTabId } from "@/contexts/TabIdContext";
|
||||
import { useTabStore } from "@/stores/tabStore";
|
||||
|
||||
function ScreenViewPage() {
|
||||
export interface ScreenViewPageProps {
|
||||
screenIdProp?: number;
|
||||
menuObjidProp?: number;
|
||||
}
|
||||
|
||||
function ScreenViewPage({ screenIdProp, menuObjidProp }: ScreenViewPageProps = {}) {
|
||||
// 스케줄 자동 생성 서비스 활성화
|
||||
const { showConfirmDialog, previewResult, handleConfirm, closeDialog, isLoading: scheduleLoading } = useScheduleGenerator();
|
||||
const params = useParams();
|
||||
const searchParams = useSearchParams();
|
||||
const router = useRouter();
|
||||
const screenId = parseInt(params.screenId as string);
|
||||
const screenId = screenIdProp ?? parseInt(params.screenId as string);
|
||||
|
||||
// URL 쿼리에서 menuObjid 가져오기 (메뉴 스코프)
|
||||
const menuObjid = searchParams.get("menuObjid") ? parseInt(searchParams.get("menuObjid")!) : undefined;
|
||||
// props 우선, 없으면 URL 쿼리에서 menuObjid 가져오기
|
||||
const menuObjid = menuObjidProp ?? (searchParams.get("menuObjid") ? parseInt(searchParams.get("menuObjid")!) : undefined);
|
||||
|
||||
// URL 쿼리에서 프리뷰용 company_code 가져오기
|
||||
const previewCompanyCode = searchParams.get("company_code");
|
||||
@@ -125,10 +132,13 @@ function ScreenViewPage() {
|
||||
initComponents();
|
||||
}, []);
|
||||
|
||||
// 편집 모달 이벤트 리스너 등록
|
||||
// 편집 모달 이벤트 리스너 등록 (활성 탭에서만 처리)
|
||||
const tabId = useTabId();
|
||||
useEffect(() => {
|
||||
const handleOpenEditModal = (event: CustomEvent) => {
|
||||
// console.log("🎭 편집 모달 열기 이벤트 수신:", event.detail);
|
||||
const state = useTabStore.getState();
|
||||
const currentActiveTabId = state[state.mode].activeTabId;
|
||||
if (tabId && tabId !== currentActiveTabId) return;
|
||||
|
||||
setEditModalConfig({
|
||||
screenId: event.detail.screenId,
|
||||
@@ -148,7 +158,7 @@ function ScreenViewPage() {
|
||||
// @ts-expect-error - CustomEvent type
|
||||
window.removeEventListener("openEditModal", handleOpenEditModal);
|
||||
};
|
||||
}, []);
|
||||
}, [tabId]);
|
||||
|
||||
useEffect(() => {
|
||||
const loadScreen = async () => {
|
||||
@@ -1344,16 +1354,17 @@ function ScreenViewPage() {
|
||||
}
|
||||
|
||||
// 실제 컴포넌트를 Provider로 감싸기
|
||||
function ScreenViewPageWrapper() {
|
||||
function ScreenViewPageWrapper({ screenIdProp, menuObjidProp }: ScreenViewPageProps = {}) {
|
||||
return (
|
||||
<TableSearchWidgetHeightProvider>
|
||||
<ScreenContextProvider>
|
||||
<SplitPanelProvider>
|
||||
<ScreenViewPage />
|
||||
<ScreenViewPage screenIdProp={screenIdProp} menuObjidProp={menuObjidProp} />
|
||||
</SplitPanelProvider>
|
||||
</ScreenContextProvider>
|
||||
</TableSearchWidgetHeightProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export { ScreenViewPageWrapper };
|
||||
export default ScreenViewPageWrapper;
|
||||
|
||||
@@ -424,4 +424,38 @@ select {
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== 모달 필수 입력 검증 ===== */
|
||||
@keyframes validationShake {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
20%, 60% { transform: translateX(-4px); }
|
||||
40%, 80% { transform: translateX(4px); }
|
||||
}
|
||||
|
||||
/* 흔들림 애니메이션 (일회성) */
|
||||
[data-validation-highlight] {
|
||||
animation: validationShake 400ms ease-in-out;
|
||||
}
|
||||
|
||||
/* 빨간 테두리 (값 입력 전까지 유지) */
|
||||
[data-validation-error] {
|
||||
border-color: hsl(var(--destructive)) !important;
|
||||
}
|
||||
|
||||
/* 필수 입력 경고 문구 (입력 필드 아래, 레이아웃 영향 없음) */
|
||||
.validation-error-msg-wrapper {
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.validation-error-msg-wrapper > p {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
left: 0;
|
||||
font-size: 11px;
|
||||
color: hsl(var(--destructive));
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* ===== End of Global Styles ===== */
|
||||
|
||||
@@ -4,7 +4,7 @@ import "./globals.css";
|
||||
import { QueryProvider } from "@/providers/QueryProvider";
|
||||
import { RegistryProvider } from "./registry-provider";
|
||||
import { Toaster } from "sonner";
|
||||
import ScreenModal from "@/components/common/ScreenModal";
|
||||
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ["latin"],
|
||||
@@ -45,7 +45,6 @@ export default function RootLayout({
|
||||
<QueryProvider>
|
||||
<RegistryProvider>{children}</RegistryProvider>
|
||||
<Toaster position="top-right" />
|
||||
<ScreenModal />
|
||||
</QueryProvider>
|
||||
{/* Portal 컨테이너 */}
|
||||
<div id="portal-root" data-radix-portal="true" />
|
||||
|
||||
Reference in New Issue
Block a user