Merge branch 'mhkim-node' of http://39.117.244.52:3000/kjs/ERP-node into jskim-node

This commit is contained in:
kjs
2026-03-17 09:46:07 +09:00
9 changed files with 594 additions and 277 deletions

View File

@@ -1338,7 +1338,6 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
size: {
...splitAdjustedComponent.size,
width: undefined as unknown as number,
height: undefined as unknown as number,
},
} : {}),
}

View File

@@ -577,7 +577,7 @@ const RealtimePreviewDynamicComponent: React.FC<RealtimePreviewProps> = ({
// - 버튼 컴포넌트: buttonElementStyle에서 자체 border 적용
const isV2HorizLabel = !!(
componentStyle &&
(componentStyle.labelDisplay === true || componentStyle.labelDisplay === "true") &&
componentStyle.labelDisplay !== false && componentStyle.labelDisplay !== "false" &&
(componentStyle.labelPosition === "left" || componentStyle.labelPosition === "right")
);
const needsStripBorder = isV2HorizLabel || isButtonComponent;

View File

@@ -34,7 +34,8 @@ const FORMAT_PATTERNS: Record<V2InputFormat, { pattern: RegExp; placeholder: str
errorMessage: "올바른 이메일 형식이 아닙니다",
},
tel: {
pattern: /^\d{2,3}-\d{3,4}-\d{4}$/,
pattern:
/^(01[016789]|02|0[3-7]1|0[3-6][2-5]|050[2-8]|070|080)-\d{3,4}-\d{4}$|^(15|16|18)\d{2}-\d{4}$/,
placeholder: "010-1234-5678",
errorMessage: "올바른 전화번호 형식이 아닙니다",
},
@@ -80,8 +81,34 @@ function formatBizNo(value: string): string {
// 전화번호 형식 변환
function formatTel(value: string): string {
const digits = value.replace(/\D/g, "");
if (digits.length === 0) return "";
// 대표번호: 15xx, 16xx, 18xx → 4-4
if (/^(15|16|18)/.test(digits)) {
if (digits.length <= 4) return digits;
return `${digits.slice(0, 4)}-${digits.slice(4, 8)}`;
}
// 서울: 02 → 2-4-4
if (digits.startsWith("02")) {
if (digits.length <= 2) return digits;
if (digits.length <= 6) return `${digits.slice(0, 2)}-${digits.slice(2)}`;
if (digits.length <= 10) return `${digits.slice(0, 2)}-${digits.slice(2, 6)}-${digits.slice(6)}`;
return `${digits.slice(0, 2)}-${digits.slice(2, 6)}-${digits.slice(6, 10)}`;
}
// 안심번호: 050x → 4-4-4
if (/^050[2-8]/.test(digits)) {
if (digits.length <= 4) return digits;
if (digits.length <= 8) return `${digits.slice(0, 4)}-${digits.slice(4)}`;
if (digits.length <= 12) return `${digits.slice(0, 4)}-${digits.slice(4, 8)}-${digits.slice(8)}`;
return `${digits.slice(0, 4)}-${digits.slice(4, 8)}-${digits.slice(8, 12)}`;
}
// 나머지 (010, 031, 070, 080 등)
if (digits.length <= 3) return digits;
if (digits.length <= 7) return `${digits.slice(0, 3)}-${digits.slice(3)}`;
if (digits.length === 10) return `${digits.slice(0, 3)}-${digits.slice(3, 6)}-${digits.slice(6)}`;
if (digits.length <= 11) return `${digits.slice(0, 3)}-${digits.slice(3, 7)}-${digits.slice(7)}`;
return `${digits.slice(0, 3)}-${digits.slice(3, 7)}-${digits.slice(7, 11)}`;
}
@@ -1175,8 +1202,8 @@ export const V2Input = forwardRef<HTMLDivElement, V2InputProps>((props, ref) =>
ref={ref}
id={id}
className={cn(
"flex flex-col gap-1",
labelPos === "left" ? "sm:flex-row sm:items-center" : "sm:flex-row-reverse sm:items-center",
"flex gap-1",
labelPos === "left" ? "flex-row items-center" : "flex-row-reverse items-center",
)}
style={{
width: componentWidth,
@@ -1191,7 +1218,7 @@ export const V2Input = forwardRef<HTMLDivElement, V2InputProps>((props, ref) =>
color: getAdaptiveLabelColor(style?.labelColor),
fontWeight: style?.labelFontWeight || "500",
}}
className="w-full text-sm font-medium whitespace-nowrap sm:w-[120px] sm:shrink-0"
className="text-sm font-medium whitespace-nowrap w-[120px] shrink-0"
>
{actualLabel}
{required && <span className="ml-0.5 text-amber-500">*</span>}

View File

@@ -1291,8 +1291,8 @@ export const V2Select = forwardRef<HTMLDivElement, V2SelectProps>((props, ref) =
ref={ref}
id={id}
className={cn(
"flex flex-col gap-1",
labelPos === "left" ? "sm:flex-row sm:items-center" : "sm:flex-row-reverse sm:items-center",
"flex gap-1",
labelPos === "left" ? "flex-row items-center" : "flex-row-reverse items-center",
isDesignMode && "pointer-events-none",
)}
style={{
@@ -1308,7 +1308,7 @@ export const V2Select = forwardRef<HTMLDivElement, V2SelectProps>((props, ref) =
color: getAdaptiveLabelColor(style?.labelColor),
fontWeight: style?.labelFontWeight || "500",
}}
className="w-full text-sm font-medium whitespace-nowrap sm:w-[120px] sm:shrink-0"
className="text-sm font-medium whitespace-nowrap w-[120px] shrink-0"
>
{label}
{required && <span className="ml-0.5 text-amber-500">*</span>}

View File

@@ -82,9 +82,10 @@ import {
arrayMove,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import type {
SplitPanelLayoutConfig,
AdditionalTabConfig,
import {
MAX_LOAD_ALL_SIZE,
type SplitPanelLayoutConfig,
type AdditionalTabConfig,
} from "@/lib/registry/components/v2-split-panel-layout/types";
import type { TableInfo, ColumnInfo } from "@/types/screen";
@@ -1158,6 +1159,41 @@ export const V2SplitPanelLayoutConfigPanel: React.FC<
updateLeftPanel({ showItemAddButton: checked })
}
/>
<SwitchRow
label="페이징 처리"
description="서버 페이지 단위 조회 (필터/정렬/계층 비활성화)"
checked={config.leftPanel?.pagination?.enabled ?? false}
onCheckedChange={(checked) =>
updateLeftPanel({
pagination: {
...config.leftPanel?.pagination,
enabled: checked,
pageSize: config.leftPanel?.pagination?.pageSize ?? 20,
},
})
}
/>
{config.leftPanel?.pagination?.enabled && (
<div className="ml-4 space-y-1">
<Label className="text-[10px]"> </Label>
<Input
type="number"
min={1}
max={MAX_LOAD_ALL_SIZE}
value={config.leftPanel?.pagination?.pageSize ?? 20}
onChange={(e) =>
updateLeftPanel({
pagination: {
...config.leftPanel?.pagination,
enabled: true,
pageSize: Math.min(MAX_LOAD_ALL_SIZE, Math.max(1, Number(e.target.value) || 20)),
},
})
}
className="h-7 w-24 text-xs"
/>
</div>
)}
</div>
{/* 좌측 패널 컬럼 설정 (접이식) */}
@@ -1564,6 +1600,41 @@ export const V2SplitPanelLayoutConfigPanel: React.FC<
updateRightPanel({ showDelete: checked })
}
/>
<SwitchRow
label="페이징 처리"
description="서버 페이지 단위 조회 (탭 포함 적용)"
checked={config.rightPanel?.pagination?.enabled ?? false}
onCheckedChange={(checked) =>
updateRightPanel({
pagination: {
...config.rightPanel?.pagination,
enabled: checked,
pageSize: config.rightPanel?.pagination?.pageSize ?? 20,
},
})
}
/>
{config.rightPanel?.pagination?.enabled && (
<div className="ml-4 space-y-1">
<Label className="text-[10px]"> </Label>
<Input
type="number"
min={1}
max={MAX_LOAD_ALL_SIZE}
value={config.rightPanel?.pagination?.pageSize ?? 20}
onChange={(e) =>
updateRightPanel({
pagination: {
...config.rightPanel?.pagination,
enabled: true,
pageSize: Math.min(MAX_LOAD_ALL_SIZE, Math.max(1, Number(e.target.value) || 20)),
},
})
}
className="h-7 w-24 text-xs"
/>
</div>
)}
</div>
{/* 우측 패널 컬럼 설정 (접이식) */}