[agent-pipeline] pipe-20260311124806-lfrk round-1
This commit is contained in:
parent
24630dd60b
commit
1bbce43ec1
|
|
@ -2,14 +2,17 @@
|
|||
|
||||
/**
|
||||
* V2Input 설정 패널
|
||||
* 통합 입력 컴포넌트의 세부 설정을 관리합니다.
|
||||
* 토스식 단계별 UX: 기본 설정 -> 타입별 설정 -> 고급 설정(접힘)
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
||||
import { Settings, ChevronDown, Loader2 } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { AutoGenerationType, AutoGenerationConfig } from "@/types/screen";
|
||||
import { AutoGenerationUtils } from "@/lib/utils/autoGeneration";
|
||||
import { getAvailableNumberingRules } from "@/lib/api/numberingRule";
|
||||
|
|
@ -29,6 +32,7 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
|
|||
const [selectedMenuObjid, setSelectedMenuObjid] = useState<number | undefined>(() => {
|
||||
return config.autoGeneration?.selectedMenuObjid || menuObjid;
|
||||
});
|
||||
const [advancedOpen, setAdvancedOpen] = useState(false);
|
||||
|
||||
const updateConfig = (field: string, value: any) => {
|
||||
onChange({ ...config, [field]: value });
|
||||
|
|
@ -76,400 +80,400 @@ export const V2InputConfigPanel: React.FC<V2InputConfigPanelProps> = ({ config,
|
|||
loadRules();
|
||||
}, [selectedMenuObjid, config.autoGeneration?.type]);
|
||||
|
||||
const inputType = config.inputType || config.type || "text";
|
||||
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
{/* INPUT TYPE 섹션 */}
|
||||
<div className="border-b border-border/50 pb-3 mb-3">
|
||||
<h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">INPUT TYPE</h4>
|
||||
<div className="flex items-center justify-between py-1.5">
|
||||
<span className="text-xs text-muted-foreground">입력 타입</span>
|
||||
<div className="w-[140px]">
|
||||
<Select
|
||||
value={config.inputType || config.type || "text"}
|
||||
onValueChange={(value) => updateConfig("inputType", value)}
|
||||
>
|
||||
<SelectTrigger className="h-7 text-xs">
|
||||
<SelectValue placeholder="입력 타입 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="text">텍스트</SelectItem>
|
||||
<SelectItem value="number">숫자</SelectItem>
|
||||
<SelectItem value="password">비밀번호</SelectItem>
|
||||
<SelectItem value="textarea">여러 줄 텍스트</SelectItem>
|
||||
<SelectItem value="slider">슬라이더</SelectItem>
|
||||
<SelectItem value="color">색상 선택</SelectItem>
|
||||
<SelectItem value="numbering">채번 (자동생성)</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
{/* ─── 1단계: 입력 타입 선택 ─── */}
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm font-medium">입력 타입</p>
|
||||
<Select
|
||||
value={inputType}
|
||||
onValueChange={(value) => updateConfig("inputType", value)}
|
||||
>
|
||||
<SelectTrigger className="h-8 text-sm">
|
||||
<SelectValue placeholder="입력 타입 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="text">텍스트</SelectItem>
|
||||
<SelectItem value="number">숫자</SelectItem>
|
||||
<SelectItem value="password">비밀번호</SelectItem>
|
||||
<SelectItem value="textarea">여러 줄 텍스트</SelectItem>
|
||||
<SelectItem value="slider">슬라이더</SelectItem>
|
||||
<SelectItem value="color">색상 선택</SelectItem>
|
||||
<SelectItem value="numbering">채번 (자동생성)</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-[11px] text-muted-foreground">입력 필드의 종류를 선택해요</p>
|
||||
</div>
|
||||
|
||||
{/* NUMBERING 섹션 - 채번 타입 전용 */}
|
||||
{config.inputType === "numbering" && (
|
||||
<div className="border-b border-border/50 pb-3 mb-3">
|
||||
<h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">NUMBERING</h4>
|
||||
<div className="rounded-md border border-primary/20 bg-primary/5 p-2 mb-2">
|
||||
<p className="text-[10px] text-primary">
|
||||
채번 규칙은 <strong>테이블 관리</strong>에서 컬럼별로 설정됩니다.
|
||||
화면에 배치된 컬럼의 채번 규칙이 자동으로 적용됩니다.
|
||||
{/* ─── 채번 타입 전용 안내 ─── */}
|
||||
{inputType === "numbering" && (
|
||||
<div className="rounded-lg border bg-muted/30 p-4 space-y-3">
|
||||
<div className="rounded-md border border-primary/20 bg-primary/5 p-3">
|
||||
<p className="text-xs text-primary">
|
||||
채번 규칙은 <strong>테이블 관리</strong>에서 컬럼별로 설정돼요.
|
||||
화면에 배치된 컬럼의 채번 규칙이 자동으로 적용돼요.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center justify-between py-1.5">
|
||||
<span className="text-xs text-muted-foreground">읽기전용 (권장)</span>
|
||||
<Checkbox
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<div>
|
||||
<p className="text-sm">읽기전용</p>
|
||||
<p className="text-[11px] text-muted-foreground">채번 필드는 자동 생성되므로 읽기전용을 권장해요</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={config.readonly !== false}
|
||||
onCheckedChange={(checked) => updateConfig("readonly", checked)}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-muted-foreground text-[10px] mt-0.5">
|
||||
채번 필드는 자동으로 생성되므로 읽기전용을 권장합니다
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 채번 타입이 아닌 경우에만 추가 설정 표시 */}
|
||||
{config.inputType !== "numbering" && (
|
||||
{/* ─── 채번 타입이 아닌 경우: 기본 설정 ─── */}
|
||||
{inputType !== "numbering" && (
|
||||
<>
|
||||
{/* FORMAT 섹션 */}
|
||||
{(config.inputType === "text" || !config.inputType) && (
|
||||
<div className="border-b border-border/50 pb-3 mb-3">
|
||||
<h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">FORMAT</h4>
|
||||
<div className="flex items-center justify-between py-1.5">
|
||||
<span className="text-xs text-muted-foreground">입력 형식</span>
|
||||
<div className="w-[140px]">
|
||||
<Select value={config.format || "none"} onValueChange={(value) => updateConfig("format", value)}>
|
||||
<SelectTrigger className="h-7 text-xs">
|
||||
<SelectValue placeholder="형식 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">제한 없음</SelectItem>
|
||||
<SelectItem value="email">이메일</SelectItem>
|
||||
<SelectItem value="tel">전화번호</SelectItem>
|
||||
<SelectItem value="url">URL</SelectItem>
|
||||
<SelectItem value="currency">통화</SelectItem>
|
||||
<SelectItem value="biz_no">사업자번호</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* PLACEHOLDER 섹션 */}
|
||||
<div className="border-b border-border/50 pb-3 mb-3">
|
||||
<h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">PLACEHOLDER</h4>
|
||||
<div className="flex items-center justify-between py-1.5">
|
||||
{/* 기본 설정 영역 */}
|
||||
<div className="rounded-lg border bg-muted/30 p-4 space-y-3">
|
||||
{/* 안내 텍스트 (placeholder) */}
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<span className="text-xs text-muted-foreground">안내 텍스트</span>
|
||||
<div className="w-[140px]">
|
||||
<Input
|
||||
value={config.placeholder || ""}
|
||||
onChange={(e) => updateConfig("placeholder", e.target.value)}
|
||||
placeholder="입력 안내"
|
||||
className="h-7 text-xs"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* RANGE 섹션 - 숫자/슬라이더 전용 */}
|
||||
{(config.inputType === "number" || config.inputType === "slider") && (
|
||||
<div className="border-b border-border/50 pb-3 mb-3">
|
||||
<h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">RANGE</h4>
|
||||
<div className="flex gap-2">
|
||||
<div className="flex-1">
|
||||
<Label className="text-[10px] text-muted-foreground">최소값</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={config.min ?? ""}
|
||||
onChange={(e) => updateConfig("min", e.target.value ? Number(e.target.value) : undefined)}
|
||||
placeholder="0"
|
||||
className="h-7 text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<Label className="text-[10px] text-muted-foreground">최대값</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={config.max ?? ""}
|
||||
onChange={(e) => updateConfig("max", e.target.value ? Number(e.target.value) : undefined)}
|
||||
placeholder="100"
|
||||
className="h-7 text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<Label className="text-[10px] text-muted-foreground">단계</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={config.step ?? ""}
|
||||
onChange={(e) => updateConfig("step", e.target.value ? Number(e.target.value) : undefined)}
|
||||
placeholder="1"
|
||||
className="h-7 text-xs"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* TEXTAREA 섹션 */}
|
||||
{config.inputType === "textarea" && (
|
||||
<div className="border-b border-border/50 pb-3 mb-3">
|
||||
<h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">TEXTAREA</h4>
|
||||
<div className="flex items-center justify-between py-1.5">
|
||||
<span className="text-xs text-muted-foreground">줄 수</span>
|
||||
<div className="w-[140px]">
|
||||
<Input
|
||||
type="number"
|
||||
value={config.rows || 3}
|
||||
onChange={(e) => updateConfig("rows", parseInt(e.target.value) || 3)}
|
||||
min={2}
|
||||
max={20}
|
||||
className="h-7 text-xs"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* INPUT MASK 섹션 */}
|
||||
<div className="border-b border-border/50 pb-3 mb-3">
|
||||
<h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">INPUT MASK</h4>
|
||||
<div className="flex items-center justify-between py-1.5">
|
||||
<span className="text-xs text-muted-foreground">마스크</span>
|
||||
<div className="w-[140px]">
|
||||
<Input
|
||||
value={config.mask || ""}
|
||||
onChange={(e) => updateConfig("mask", e.target.value)}
|
||||
placeholder="###-####-####"
|
||||
className="h-7 text-xs"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-muted-foreground text-[10px] mt-0.5"># = 숫자, A = 문자, * = 모든 문자</p>
|
||||
</div>
|
||||
|
||||
{/* AUTO GENERATION 섹션 */}
|
||||
<div className="border-b border-border/50 pb-3 mb-3">
|
||||
<h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">AUTO GENERATION</h4>
|
||||
<div className="flex items-center justify-between py-1.5">
|
||||
<span className="text-xs text-muted-foreground">자동생성 활성화</span>
|
||||
<Checkbox
|
||||
checked={config.autoGeneration?.enabled || false}
|
||||
onCheckedChange={(checked) => {
|
||||
const currentConfig = config.autoGeneration || { type: "none", enabled: false };
|
||||
updateConfig("autoGeneration", {
|
||||
...currentConfig,
|
||||
enabled: checked as boolean,
|
||||
});
|
||||
}}
|
||||
<Input
|
||||
value={config.placeholder || ""}
|
||||
onChange={(e) => updateConfig("placeholder", e.target.value)}
|
||||
placeholder="입력 안내"
|
||||
className="h-7 w-[160px] text-xs"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{config.autoGeneration?.enabled && (
|
||||
<div className="mt-1 space-y-1">
|
||||
{/* 자동생성 타입 */}
|
||||
<div className="flex items-center justify-between py-1.5">
|
||||
<span className="text-xs text-muted-foreground">타입</span>
|
||||
<div className="w-[140px]">
|
||||
<Select
|
||||
value={config.autoGeneration?.type || "none"}
|
||||
onValueChange={(value: AutoGenerationType) => {
|
||||
const currentConfig = config.autoGeneration || { type: "none", enabled: false };
|
||||
updateConfig("autoGeneration", {
|
||||
...currentConfig,
|
||||
type: value,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="h-7 text-xs">
|
||||
<SelectValue placeholder="자동생성 타입 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">자동생성 없음</SelectItem>
|
||||
<SelectItem value="uuid">UUID 생성</SelectItem>
|
||||
<SelectItem value="current_user">현재 사용자 ID</SelectItem>
|
||||
<SelectItem value="current_time">현재 시간</SelectItem>
|
||||
<SelectItem value="sequence">순차 번호</SelectItem>
|
||||
<SelectItem value="numbering_rule">채번 규칙</SelectItem>
|
||||
<SelectItem value="random_string">랜덤 문자열</SelectItem>
|
||||
<SelectItem value="random_number">랜덤 숫자</SelectItem>
|
||||
<SelectItem value="company_code">회사 코드</SelectItem>
|
||||
<SelectItem value="department">부서 코드</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{/* 입력 형식 - 텍스트 타입 전용 */}
|
||||
{(inputType === "text" || !config.inputType) && (
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<span className="text-xs text-muted-foreground">입력 형식</span>
|
||||
<Select value={config.format || "none"} onValueChange={(value) => updateConfig("format", value)}>
|
||||
<SelectTrigger className="h-7 w-[160px] text-xs">
|
||||
<SelectValue placeholder="형식 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">제한 없음</SelectItem>
|
||||
<SelectItem value="email">이메일</SelectItem>
|
||||
<SelectItem value="tel">전화번호</SelectItem>
|
||||
<SelectItem value="url">URL</SelectItem>
|
||||
<SelectItem value="currency">통화</SelectItem>
|
||||
<SelectItem value="biz_no">사업자번호</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 입력 마스크 */}
|
||||
<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>
|
||||
</div>
|
||||
<Input
|
||||
value={config.mask || ""}
|
||||
onChange={(e) => updateConfig("mask", e.target.value)}
|
||||
placeholder="###-####-####"
|
||||
className="h-7 w-[160px] text-xs"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 숫자/슬라이더: 범위 설정 */}
|
||||
{(inputType === "number" || inputType === "slider") && (
|
||||
<div className="space-y-2 pt-1">
|
||||
<p className="text-xs text-muted-foreground">값 범위</p>
|
||||
<div className="flex gap-2">
|
||||
<div className="flex-1">
|
||||
<Label className="text-[10px] text-muted-foreground">최소값</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={config.min ?? ""}
|
||||
onChange={(e) => updateConfig("min", e.target.value ? Number(e.target.value) : undefined)}
|
||||
placeholder="0"
|
||||
className="h-7 text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<Label className="text-[10px] text-muted-foreground">최대값</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={config.max ?? ""}
|
||||
onChange={(e) => updateConfig("max", e.target.value ? Number(e.target.value) : undefined)}
|
||||
placeholder="100"
|
||||
className="h-7 text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<Label className="text-[10px] text-muted-foreground">단계</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={config.step ?? ""}
|
||||
onChange={(e) => updateConfig("step", e.target.value ? Number(e.target.value) : undefined)}
|
||||
placeholder="1"
|
||||
className="h-7 text-xs"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{config.autoGeneration?.type && config.autoGeneration.type !== "none" && (
|
||||
<p className="text-muted-foreground text-[10px] mt-0.5">
|
||||
{AutoGenerationUtils.getTypeDescription(config.autoGeneration.type)}
|
||||
</p>
|
||||
)}
|
||||
{/* 여러 줄 텍스트: 줄 수 */}
|
||||
{inputType === "textarea" && (
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<span className="text-xs text-muted-foreground">줄 수</span>
|
||||
<Input
|
||||
type="number"
|
||||
value={config.rows || 3}
|
||||
onChange={(e) => updateConfig("rows", parseInt(e.target.value) || 3)}
|
||||
min={2}
|
||||
max={20}
|
||||
className="h-7 w-[160px] text-xs"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 채번 규칙 선택 */}
|
||||
{config.autoGeneration?.type === "numbering_rule" && (
|
||||
<div className="mt-1 space-y-1">
|
||||
<div className="flex items-center justify-between py-1.5">
|
||||
<span className="text-xs text-muted-foreground">
|
||||
대상 메뉴 <span className="text-destructive">*</span>
|
||||
</span>
|
||||
<div className="w-[140px]">
|
||||
<Select
|
||||
value={selectedMenuObjid?.toString() || ""}
|
||||
onValueChange={(value) => {
|
||||
const menuId = parseInt(value);
|
||||
setSelectedMenuObjid(menuId);
|
||||
updateConfig("autoGeneration", {
|
||||
...config.autoGeneration,
|
||||
selectedMenuObjid: menuId,
|
||||
});
|
||||
}}
|
||||
disabled={loadingMenus}
|
||||
>
|
||||
<SelectTrigger className="h-7 text-xs">
|
||||
<SelectValue placeholder={loadingMenus ? "로딩 중..." : "메뉴 선택"} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{parentMenus.length === 0 ? (
|
||||
<SelectItem value="no-menus" disabled>
|
||||
사용 가능한 메뉴가 없습니다
|
||||
</SelectItem>
|
||||
) : (
|
||||
parentMenus.map((menu) => (
|
||||
<SelectItem key={menu.objid} value={menu.objid.toString()}>
|
||||
{menu.menu_name_kor}
|
||||
</SelectItem>
|
||||
))
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
{/* ─── 고급 설정: 자동 생성 (Collapsible) ─── */}
|
||||
<Collapsible open={advancedOpen} onOpenChange={setAdvancedOpen}>
|
||||
<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"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Settings className="h-4 w-4 text-muted-foreground" />
|
||||
<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"
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent>
|
||||
<div className="rounded-b-lg border border-t-0 p-4 space-y-3">
|
||||
{/* 자동 생성 토글 */}
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<div>
|
||||
<p className="text-sm">자동 생성</p>
|
||||
<p className="text-[11px] text-muted-foreground">값이 자동으로 채워져요</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={config.autoGeneration?.enabled || false}
|
||||
onCheckedChange={(checked) => {
|
||||
const currentConfig = config.autoGeneration || { type: "none", enabled: false };
|
||||
updateConfig("autoGeneration", {
|
||||
...currentConfig,
|
||||
enabled: checked as boolean,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{config.autoGeneration?.enabled && (
|
||||
<div className="space-y-3 ml-1 border-l-2 border-primary/20 pl-3">
|
||||
{/* 자동 생성 타입 */}
|
||||
<div>
|
||||
<p className="mb-1.5 text-xs text-muted-foreground">생성 방식</p>
|
||||
<Select
|
||||
value={config.autoGeneration?.type || "none"}
|
||||
onValueChange={(value: AutoGenerationType) => {
|
||||
const currentConfig = config.autoGeneration || { type: "none", enabled: false };
|
||||
updateConfig("autoGeneration", {
|
||||
...currentConfig,
|
||||
type: value,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="h-8 text-sm">
|
||||
<SelectValue placeholder="자동생성 타입 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">자동생성 없음</SelectItem>
|
||||
<SelectItem value="uuid">UUID 생성</SelectItem>
|
||||
<SelectItem value="current_user">현재 사용자 ID</SelectItem>
|
||||
<SelectItem value="current_time">현재 시간</SelectItem>
|
||||
<SelectItem value="sequence">순차 번호</SelectItem>
|
||||
<SelectItem value="numbering_rule">채번 규칙</SelectItem>
|
||||
<SelectItem value="random_string">랜덤 문자열</SelectItem>
|
||||
<SelectItem value="random_number">랜덤 숫자</SelectItem>
|
||||
<SelectItem value="company_code">회사 코드</SelectItem>
|
||||
<SelectItem value="department">부서 코드</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{selectedMenuObjid ? (
|
||||
<div className="flex items-center justify-between py-1.5">
|
||||
<span className="text-xs text-muted-foreground">
|
||||
채번 규칙 <span className="text-destructive">*</span>
|
||||
</span>
|
||||
<div className="w-[140px]">
|
||||
{config.autoGeneration?.type && config.autoGeneration.type !== "none" && (
|
||||
<p className="text-[11px] text-muted-foreground">
|
||||
{AutoGenerationUtils.getTypeDescription(config.autoGeneration.type)}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* 채번 규칙 선택 */}
|
||||
{config.autoGeneration?.type === "numbering_rule" && (
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<p className="mb-1.5 text-xs text-muted-foreground">
|
||||
대상 메뉴 <span className="text-destructive">*</span>
|
||||
</p>
|
||||
<Select
|
||||
value={config.autoGeneration?.options?.numberingRuleId || ""}
|
||||
value={selectedMenuObjid?.toString() || ""}
|
||||
onValueChange={(value) => {
|
||||
const menuId = parseInt(value);
|
||||
setSelectedMenuObjid(menuId);
|
||||
updateConfig("autoGeneration", {
|
||||
...config.autoGeneration,
|
||||
options: {
|
||||
...config.autoGeneration?.options,
|
||||
numberingRuleId: value,
|
||||
},
|
||||
selectedMenuObjid: menuId,
|
||||
});
|
||||
}}
|
||||
disabled={loadingRules}
|
||||
disabled={loadingMenus}
|
||||
>
|
||||
<SelectTrigger className="h-7 text-xs">
|
||||
<SelectValue placeholder={loadingRules ? "로딩 중..." : "규칙 선택"} />
|
||||
<SelectTrigger className="h-8 text-sm">
|
||||
<SelectValue placeholder={loadingMenus ? "로딩 중..." : "메뉴 선택"} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{numberingRules.length === 0 ? (
|
||||
<SelectItem value="no-rules" disabled>
|
||||
사용 가능한 규칙이 없습니다
|
||||
{parentMenus.length === 0 ? (
|
||||
<SelectItem value="no-menus" disabled>
|
||||
사용 가능한 메뉴가 없습니다
|
||||
</SelectItem>
|
||||
) : (
|
||||
numberingRules.map((rule) => (
|
||||
<SelectItem key={rule.ruleId} value={rule.ruleId}>
|
||||
{rule.ruleName}
|
||||
parentMenus.map((menu) => (
|
||||
<SelectItem key={menu.objid} value={menu.objid.toString()}>
|
||||
{menu.menu_name_kor}
|
||||
</SelectItem>
|
||||
))
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-md border border-amber-200 bg-amber-50 p-2 text-[10px] text-amber-800">
|
||||
먼저 대상 메뉴를 선택하세요
|
||||
|
||||
{selectedMenuObjid ? (
|
||||
<div>
|
||||
<p className="mb-1.5 text-xs text-muted-foreground">
|
||||
채번 규칙 <span className="text-destructive">*</span>
|
||||
</p>
|
||||
{loadingRules ? (
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<Loader2 className="h-3 w-3 animate-spin" />
|
||||
규칙 로딩 중...
|
||||
</div>
|
||||
) : (
|
||||
<Select
|
||||
value={config.autoGeneration?.options?.numberingRuleId || ""}
|
||||
onValueChange={(value) => {
|
||||
updateConfig("autoGeneration", {
|
||||
...config.autoGeneration,
|
||||
options: {
|
||||
...config.autoGeneration?.options,
|
||||
numberingRuleId: value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="h-8 text-sm">
|
||||
<SelectValue placeholder="규칙 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{numberingRules.length === 0 ? (
|
||||
<SelectItem value="no-rules" disabled>
|
||||
사용 가능한 규칙이 없습니다
|
||||
</SelectItem>
|
||||
) : (
|
||||
numberingRules.map((rule) => (
|
||||
<SelectItem key={rule.ruleId} value={rule.ruleId}>
|
||||
{rule.ruleName}
|
||||
</SelectItem>
|
||||
))
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-md border border-amber-200 bg-amber-50 p-2.5 text-xs text-amber-800">
|
||||
먼저 대상 메뉴를 선택하세요
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 자동생성 옵션 (랜덤/순차용) */}
|
||||
{config.autoGeneration?.type &&
|
||||
["random_string", "random_number", "sequence"].includes(config.autoGeneration.type) && (
|
||||
<div className="mt-1 space-y-1">
|
||||
{["random_string", "random_number"].includes(config.autoGeneration.type) && (
|
||||
<div className="flex items-center justify-between py-1.5">
|
||||
<span className="text-xs text-muted-foreground">길이</span>
|
||||
<div className="w-[140px]">
|
||||
{/* 랜덤/순차 옵션 */}
|
||||
{config.autoGeneration?.type &&
|
||||
["random_string", "random_number", "sequence"].includes(config.autoGeneration.type) && (
|
||||
<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>
|
||||
<Input
|
||||
type="number"
|
||||
min="1"
|
||||
max="50"
|
||||
value={config.autoGeneration?.options?.length || 8}
|
||||
onChange={(e) => {
|
||||
updateConfig("autoGeneration", {
|
||||
...config.autoGeneration,
|
||||
options: {
|
||||
...config.autoGeneration?.options,
|
||||
length: parseInt(e.target.value) || 8,
|
||||
},
|
||||
});
|
||||
}}
|
||||
className="h-7 w-[120px] text-xs"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<span className="text-xs text-muted-foreground">접두사</span>
|
||||
<Input
|
||||
type="number"
|
||||
min="1"
|
||||
max="50"
|
||||
value={config.autoGeneration?.options?.length || 8}
|
||||
value={config.autoGeneration?.options?.prefix || ""}
|
||||
onChange={(e) => {
|
||||
updateConfig("autoGeneration", {
|
||||
...config.autoGeneration,
|
||||
options: {
|
||||
...config.autoGeneration?.options,
|
||||
length: parseInt(e.target.value) || 8,
|
||||
prefix: e.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
className="h-7 text-xs"
|
||||
placeholder="예: INV-"
|
||||
className="h-7 w-[120px] text-xs"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<span className="text-xs text-muted-foreground">접미사</span>
|
||||
<Input
|
||||
value={config.autoGeneration?.options?.suffix || ""}
|
||||
onChange={(e) => {
|
||||
updateConfig("autoGeneration", {
|
||||
...config.autoGeneration,
|
||||
options: {
|
||||
...config.autoGeneration?.options,
|
||||
suffix: e.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
className="h-7 w-[120px] text-xs"
|
||||
/>
|
||||
</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">
|
||||
{AutoGenerationUtils.generatePreviewValue(config.autoGeneration)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-between py-1.5">
|
||||
<span className="text-xs text-muted-foreground">접두사</span>
|
||||
<div className="w-[140px]">
|
||||
<Input
|
||||
value={config.autoGeneration?.options?.prefix || ""}
|
||||
onChange={(e) => {
|
||||
updateConfig("autoGeneration", {
|
||||
...config.autoGeneration,
|
||||
options: {
|
||||
...config.autoGeneration?.options,
|
||||
prefix: e.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
placeholder="예: INV-"
|
||||
className="h-7 text-xs"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between py-1.5">
|
||||
<span className="text-xs text-muted-foreground">접미사</span>
|
||||
<div className="w-[140px]">
|
||||
<Input
|
||||
value={config.autoGeneration?.options?.suffix || ""}
|
||||
onChange={(e) => {
|
||||
updateConfig("autoGeneration", {
|
||||
...config.autoGeneration,
|
||||
options: {
|
||||
...config.autoGeneration?.options,
|
||||
suffix: e.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
className="h-7 text-xs"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="py-1.5">
|
||||
<span className="text-[10px] text-muted-foreground">미리보기</span>
|
||||
<div className="mt-1 rounded border bg-muted p-1.5 text-xs font-mono">
|
||||
{AutoGenerationUtils.generatePreviewValue(config.autoGeneration)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue