Compare commits

..

4 Commits

Author SHA1 Message Date
kjs efd3e2a0cd fix: update V2PropertiesPanel to use V2FieldConfigPanel for input and select components
- Replaced references to V2InputConfigPanel and V2SelectConfigPanel with V2FieldConfigPanel in the V2PropertiesPanel.
- This change ensures consistent configuration handling for both input and select components, improving maintainability and usability.

Made-with: Cursor
2026-03-12 15:46:54 +09:00
kjs 884cde463f feat: enhance V2InputConfigPanel with additional UI components
- Added Separator and Checkbox components to the V2InputConfigPanel for improved layout and functionality.
- This enhancement aims to provide better user interaction and organization within the input configuration settings.

Made-with: Cursor
2026-03-12 15:43:06 +09:00
kjs c757ea1733 chore: remove peer dependencies from package-lock.json
- Removed unnecessary "peer" entries from various packages in package-lock.json to streamline dependency management and avoid potential conflicts.
- This cleanup helps maintain a cleaner and more efficient package structure.

Made-with: Cursor
2026-03-12 15:12:26 +09:00
kjs ca390bb191 Merge branch 'gbpark-node' of http://39.117.244.52:3000/kjs/ERP-node into jskim-node 2026-03-12 15:09:55 +09:00
2 changed files with 988 additions and 831 deletions

View File

@ -13,7 +13,18 @@ import { Switch } from "@/components/ui/switch";
import { Separator } from "@/components/ui/separator";
import { Checkbox } from "@/components/ui/checkbox";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import { Settings, ChevronDown, Loader2, Type, Hash, Lock, AlignLeft, SlidersHorizontal, Palette, ListOrdered } from "lucide-react";
import {
Settings,
ChevronDown,
Loader2,
Type,
Hash,
Lock,
AlignLeft,
SlidersHorizontal,
Palette,
ListOrdered,
} from "lucide-react";
import { cn } from "@/lib/utils";
import { AutoGenerationType, AutoGenerationConfig } from "@/types/screen";
import { AutoGenerationUtils } from "@/lib/utils/autoGeneration";
@ -24,9 +35,15 @@ interface V2InputConfigPanelProps {
config: Record<string, any>;
onChange: (config: Record<string, any>) => void;
menuObjid?: number;
allComponents?: any[];
}
export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config, onChange, menuObjid }) => {
export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({
config,
onChange,
menuObjid,
allComponents = [],
}) => {
const [numberingRules, setNumberingRules] = useState<NumberingRuleConfig[]>([]);
const [loadingRules, setLoadingRules] = useState(false);
const [parentMenus, setParentMenus] = useState<any[]>([]);
@ -51,7 +68,7 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
const userMenus = allMenus.filter((menu: any) => {
const menuType = menu.menu_type || menu.menuType;
const level = menu.level || menu.lev || menu.LEVEL;
return menuType === '1' && (level === 2 || level === 3 || level === '2' || level === '3');
return menuType === "1" && (level === 2 || level === 3 || level === "2" || level === "3");
});
setParentMenus(userMenus);
}
@ -70,7 +87,10 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
const loadRules = async () => {
const isNumbering = inputType === "numbering" || config.autoGeneration?.type === "numbering_rule";
if (!isNumbering) return;
if (!selectedMenuObjid) { setNumberingRules([]); return; }
if (!selectedMenuObjid) {
setNumberingRules([]);
return;
}
setLoadingRules(true);
try {
const response = await getAvailableNumberingRules(selectedMenuObjid);
@ -92,10 +112,10 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
{/* ─── 1단계: 입력 타입 선택 (카드 방식) ─── */}
<div className="space-y-2">
<div className="flex items-center gap-2">
<Type className="h-4 w-4 text-muted-foreground" />
<Type className="text-muted-foreground h-4 w-4" />
<p className="text-sm font-medium"> </p>
</div>
<p className="text-[11px] text-muted-foreground"> </p>
<p className="text-muted-foreground text-[11px]"> </p>
</div>
<div className="grid grid-cols-2 gap-2">
@ -132,20 +152,23 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
className={cn(
"flex items-center gap-2 rounded-lg border p-2.5 text-left transition-all",
inputType === item.value
? "border-primary bg-primary/5 ring-1 ring-primary/20"
: "border-border hover:border-primary/30 hover:bg-muted/30"
? "border-primary bg-primary/5 ring-primary/20 ring-1"
: "border-border hover:border-primary/30 hover:bg-muted/30",
)}
>
<item.icon className={cn(
"h-4 w-4 shrink-0",
inputType === item.value ? "text-primary" : "text-muted-foreground"
)} />
<item.icon
className={cn("h-4 w-4 shrink-0", inputType === item.value ? "text-primary" : "text-muted-foreground")}
/>
<div className="min-w-0">
<span className={cn(
"text-xs font-medium block",
inputType === item.value ? "text-primary" : "text-foreground"
)}>{item.label}</span>
<span className="text-[10px] text-muted-foreground block truncate">{item.desc}</span>
<span
className={cn(
"block text-xs font-medium",
inputType === item.value ? "text-primary" : "text-foreground",
)}
>
{item.label}
</span>
<span className="text-muted-foreground block truncate text-[10px]">{item.desc}</span>
</div>
</button>
))}
@ -153,34 +176,34 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
{/* ─── 채번 타입 전용 설정 ─── */}
{inputType === "numbering" && (
<div className="rounded-lg border bg-muted/30 p-4 space-y-3">
<div className="bg-muted/30 space-y-3 rounded-lg border p-4">
<div className="flex items-center gap-2">
<ListOrdered className="h-4 w-4 text-primary" />
<ListOrdered className="text-primary h-4 w-4" />
<span className="text-sm font-medium"> </span>
</div>
<div>
<p className="mb-1.5 text-xs text-muted-foreground"> </p>
<p className="text-muted-foreground mb-1.5 text-xs"> </p>
{menuObjid && selectedMenuObjid === menuObjid ? (
<div className="rounded-md border bg-background p-2">
<p className="text-xs text-muted-foreground"> </p>
<div className="bg-background rounded-md border p-2">
<p className="text-muted-foreground text-xs"> </p>
<div className="mt-1 flex items-center justify-between">
<p className="text-sm font-medium">
{parentMenus.find((m: any) => m.objid === menuObjid)?.menu_name_kor
|| parentMenus.find((m: any) => m.objid === menuObjid)?.translated_name
|| `메뉴 #${menuObjid}`}
{parentMenus.find((m: any) => m.objid === menuObjid)?.menu_name_kor ||
parentMenus.find((m: any) => m.objid === menuObjid)?.translated_name ||
`메뉴 #${menuObjid}`}
</p>
<button
type="button"
onClick={() => setSelectedMenuObjid(undefined)}
className="text-[10px] text-muted-foreground hover:text-foreground"
className="text-muted-foreground hover:text-foreground text-[10px]"
>
</button>
</div>
</div>
) : loadingMenus ? (
<div className="text-muted-foreground flex items-center gap-2 text-xs py-1">
<div className="text-muted-foreground flex items-center gap-2 py-1 text-xs">
<Loader2 className="h-3 w-3 animate-spin" />
...
</div>
@ -216,9 +239,9 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
{selectedMenuObjid && (
<div>
<p className="mb-1.5 text-xs text-muted-foreground"> </p>
<p className="text-muted-foreground mb-1.5 text-xs"> </p>
{loadingRules ? (
<div className="text-muted-foreground flex items-center gap-2 text-xs py-1">
<div className="text-muted-foreground flex items-center gap-2 py-1 text-xs">
<Loader2 className="h-3 w-3 animate-spin" />
...
</div>
@ -243,13 +266,14 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
<SelectContent>
{numberingRules.map((rule) => (
<SelectItem key={rule.ruleId} value={String(rule.ruleId)}>
{rule.ruleName} ({rule.separator || "-"}{"{번호}"})
{rule.ruleName} ({rule.separator || "-"}
{"{번호}"})
</SelectItem>
))}
</SelectContent>
</Select>
) : (
<p className="text-xs text-muted-foreground"> </p>
<p className="text-muted-foreground text-xs"> </p>
)}
</div>
)}
@ -257,7 +281,7 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
<div className="flex items-center justify-between py-1">
<div>
<p className="text-sm"></p>
<p className="text-[11px] text-muted-foreground"> </p>
<p className="text-muted-foreground text-[11px]"> </p>
</div>
<Switch
checked={config.readonly !== false}
@ -271,10 +295,10 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
{inputType !== "numbering" && (
<>
{/* 기본 설정 영역 */}
<div className="rounded-lg border bg-muted/30 p-4 space-y-3">
<div className="bg-muted/30 space-y-3 rounded-lg border p-4">
{/* 안내 텍스트 (placeholder) */}
<div className="flex items-center justify-between py-1">
<span className="text-xs text-muted-foreground"> </span>
<span className="text-muted-foreground text-xs"> </span>
<Input
value={config.placeholder || ""}
onChange={(e) => updateConfig("placeholder", e.target.value)}
@ -286,7 +310,7 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
{/* 입력 형식 - 텍스트 타입 전용 */}
{(inputType === "text" || !config.inputType) && (
<div className="flex items-center justify-between py-1">
<span className="text-xs text-muted-foreground"> </span>
<span className="text-muted-foreground text-xs"> </span>
<Select value={config.format || "none"} onValueChange={(value) => updateConfig("format", value)}>
<SelectTrigger className="h-7 w-[160px] text-xs">
<SelectValue placeholder="형식 선택" />
@ -306,8 +330,8 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
{/* 입력 마스크 */}
<div className="flex items-center justify-between py-1">
<div>
<span className="text-xs text-muted-foreground"> </span>
<p className="text-[10px] text-muted-foreground mt-0.5"># = , A = , * = </p>
<span className="text-muted-foreground text-xs"> </span>
<p className="text-muted-foreground mt-0.5 text-[10px]"># = , A = , * = </p>
</div>
<Input
value={config.mask || ""}
@ -320,10 +344,10 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
{/* 숫자/슬라이더: 범위 설정 */}
{(inputType === "number" || inputType === "slider") && (
<div className="space-y-2 pt-1">
<p className="text-xs text-muted-foreground"> </p>
<p className="text-muted-foreground text-xs"> </p>
<div className="flex gap-2">
<div className="flex-1">
<Label className="text-[10px] text-muted-foreground"></Label>
<Label className="text-muted-foreground text-[10px]"></Label>
<Input
type="number"
value={config.min ?? ""}
@ -333,7 +357,7 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
/>
</div>
<div className="flex-1">
<Label className="text-[10px] text-muted-foreground"></Label>
<Label className="text-muted-foreground text-[10px]"></Label>
<Input
type="number"
value={config.max ?? ""}
@ -343,7 +367,7 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
/>
</div>
<div className="flex-1">
<Label className="text-[10px] text-muted-foreground"></Label>
<Label className="text-muted-foreground text-[10px]"></Label>
<Input
type="number"
value={config.step ?? ""}
@ -359,7 +383,7 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
{/* 여러 줄 텍스트: 줄 수 */}
{inputType === "textarea" && (
<div className="flex items-center justify-between py-1">
<span className="text-xs text-muted-foreground"> </span>
<span className="text-muted-foreground text-xs"> </span>
<Input
type="number"
value={config.rows || 3}
@ -377,27 +401,27 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
<CollapsibleTrigger asChild>
<button
type="button"
className="flex w-full items-center justify-between rounded-lg border bg-muted/30 px-4 py-2.5 text-left transition-colors hover:bg-muted/50"
className="bg-muted/30 hover:bg-muted/50 flex w-full items-center justify-between rounded-lg border px-4 py-2.5 text-left transition-colors"
>
<div className="flex items-center gap-2">
<Settings className="h-4 w-4 text-muted-foreground" />
<Settings className="text-muted-foreground h-4 w-4" />
<span className="text-sm font-medium"> </span>
</div>
<ChevronDown
className={cn(
"h-4 w-4 text-muted-foreground transition-transform duration-200",
advancedOpen && "rotate-180"
"text-muted-foreground h-4 w-4 transition-transform duration-200",
advancedOpen && "rotate-180",
)}
/>
</button>
</CollapsibleTrigger>
<CollapsibleContent>
<div className="rounded-b-lg border border-t-0 p-4 space-y-3">
<div className="space-y-3 rounded-b-lg border border-t-0 p-4">
{/* 자동 생성 토글 */}
<div className="flex items-center justify-between py-1">
<div>
<p className="text-sm"> </p>
<p className="text-[11px] text-muted-foreground"> </p>
<p className="text-muted-foreground text-[11px]"> </p>
</div>
<Switch
checked={config.autoGeneration?.enabled || false}
@ -412,10 +436,10 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
</div>
{config.autoGeneration?.enabled && (
<div className="space-y-3 ml-1 border-l-2 border-primary/20 pl-3">
<div className="border-primary/20 ml-1 space-y-3 border-l-2 pl-3">
{/* 자동 생성 타입 */}
<div>
<p className="mb-1.5 text-xs text-muted-foreground"> </p>
<p className="text-muted-foreground mb-1.5 text-xs"> </p>
<Select
value={config.autoGeneration?.type || "none"}
onValueChange={(value: AutoGenerationType) => {
@ -445,7 +469,7 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
</div>
{config.autoGeneration?.type && config.autoGeneration.type !== "none" && (
<p className="text-[11px] text-muted-foreground">
<p className="text-muted-foreground text-[11px]">
{AutoGenerationUtils.getTypeDescription(config.autoGeneration.type)}
</p>
)}
@ -454,7 +478,7 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
{config.autoGeneration?.type === "numbering_rule" && (
<div className="space-y-3">
<div>
<p className="mb-1.5 text-xs text-muted-foreground">
<p className="text-muted-foreground mb-1.5 text-xs">
<span className="text-destructive">*</span>
</p>
<Select
@ -490,11 +514,11 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
{selectedMenuObjid ? (
<div>
<p className="mb-1.5 text-xs text-muted-foreground">
<p className="text-muted-foreground mb-1.5 text-xs">
<span className="text-destructive">*</span>
</p>
{loadingRules ? (
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<div className="text-muted-foreground flex items-center gap-2 text-xs">
<Loader2 className="h-3 w-3 animate-spin" />
...
</div>
@ -544,7 +568,7 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
<div className="space-y-3">
{["random_string", "random_number"].includes(config.autoGeneration.type) && (
<div className="flex items-center justify-between py-1">
<span className="text-xs text-muted-foreground"></span>
<span className="text-muted-foreground text-xs"></span>
<Input
type="number"
min="1"
@ -565,7 +589,7 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
)}
<div className="flex items-center justify-between py-1">
<span className="text-xs text-muted-foreground"></span>
<span className="text-muted-foreground text-xs"></span>
<Input
value={config.autoGeneration?.options?.prefix || ""}
onChange={(e) => {
@ -583,7 +607,7 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
</div>
<div className="flex items-center justify-between py-1">
<span className="text-xs text-muted-foreground"></span>
<span className="text-muted-foreground text-xs"></span>
<Input
value={config.autoGeneration?.options?.suffix || ""}
onChange={(e) => {
@ -600,8 +624,8 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
</div>
<div>
<span className="text-xs text-muted-foreground"></span>
<div className="mt-1 rounded-md border bg-muted p-2 text-xs font-mono">
<span className="text-muted-foreground text-xs"></span>
<div className="bg-muted mt-1 rounded-md border p-2 font-mono text-xs">
{AutoGenerationUtils.generatePreviewValue(config.autoGeneration)}
</div>
</div>
@ -644,10 +668,7 @@ function DataBindingSection({
const tableListComponents = React.useMemo(() => {
return allComponents.filter((comp) => {
const type =
comp.componentType ||
comp.widgetType ||
comp.componentConfig?.type ||
(comp.url && comp.url.split("/").pop());
comp.componentType || comp.widgetType || comp.componentConfig?.type || (comp.url && comp.url.split("/").pop());
return type === "v2-table-list";
});
}, [allComponents]);
@ -660,11 +681,7 @@ function DataBindingSection({
const selectedTableName = React.useMemo(() => {
if (!selectedTableComponent) return null;
return (
selectedTableComponent.componentConfig?.selectedTable ||
selectedTableComponent.selectedTable ||
null
);
return selectedTableComponent.componentConfig?.selectedTable || selectedTableComponent.selectedTable || null;
}, [selectedTableComponent]);
// 선택된 테이블의 컬럼 목록 로드
@ -725,9 +742,7 @@ function DataBindingSection({
{config.dataBinding && (
<div className="space-y-2 rounded border p-2">
<p className="text-[10px] text-muted-foreground">
</p>
<p className="text-muted-foreground text-[10px]"> </p>
{/* 소스 테이블 컴포넌트 선택 */}
<div className="space-y-1">
@ -750,8 +765,7 @@ function DataBindingSection({
</SelectTrigger>
<SelectContent>
{tableListComponents.map((comp) => {
const tblName =
comp.componentConfig?.selectedTable || comp.selectedTable || "";
const tblName = comp.componentConfig?.selectedTable || comp.selectedTable || "";
const label = comp.componentConfig?.label || comp.label || comp.id;
return (
<SelectItem key={comp.id} value={comp.id}>
@ -769,7 +783,7 @@ function DataBindingSection({
<div className="space-y-1">
<Label className="text-xs font-medium"> </Label>
{loadingColumns ? (
<p className="text-[10px] text-muted-foreground"> ...</p>
<p className="text-muted-foreground text-[10px]"> ...</p>
) : tableColumns.length === 0 ? (
<>
<Input
@ -783,7 +797,7 @@ function DataBindingSection({
placeholder="컬럼명 직접 입력"
className="h-7 text-xs"
/>
<p className="text-[10px] text-muted-foreground"> </p>
<p className="text-muted-foreground text-[10px]"> </p>
</>
) : (
<Select

File diff suppressed because it is too large Load Diff