"use client";
/**
* V2ComponentsDemo
*
* V2 컴포넌트들을 테스트하고 미리볼 수 있는 데모 페이지
*/
import React, { useState } from "react";
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { ArrowLeft, Code, Eye, Zap } from "lucide-react";
// V2 컴포넌트들
import { V2Input } from "./V2Input";
import { V2Select } from "./V2Select";
import { V2Date } from "./V2Date";
import { V2List } from "./V2List";
import { V2Layout } from "./V2Layout";
import { V2Group } from "./V2Group";
import { V2Media } from "./V2Media";
import { V2Biz } from "./V2Biz";
import { V2Hierarchy } from "./V2Hierarchy";
// 조건부 로직
import { V2FormProvider, useV2Form } from "./V2FormContext";
import { ConditionalConfigPanel } from "./ConditionalConfigPanel";
// 타입
import { HierarchyNode, ConditionalConfig } from "@/types/v2-components";
interface V2ComponentsDemoProps {
onBack?: () => void;
}
export function V2ComponentsDemo({ onBack }: V2ComponentsDemoProps) {
const [activeTab, setActiveTab] = useState("conditional");
// 데모용 상태
const [inputValues, setInputValues] = useState({
text: "",
number: 50,
password: "",
slider: 30,
color: "#3b82f6",
});
const [selectValues, setSelectValues] = useState({
dropdown: "",
radio: "",
check: [] as string[],
tag: [] as string[],
toggle: "false",
});
const [dateValues, setDateValues] = useState({
date: "",
time: "",
datetime: "",
range: ["", ""] as [string, string],
});
// 샘플 데이터
const sampleOptions = [
{ value: "option1", label: "옵션 1" },
{ value: "option2", label: "옵션 2" },
{ value: "option3", label: "옵션 3" },
{ value: "option4", label: "옵션 4" },
];
const sampleTableData = [
{ id: 1, name: "홍길동", email: "hong@test.com", status: "active", date: "2024-01-15" },
{ id: 2, name: "김철수", email: "kim@test.com", status: "inactive", date: "2024-02-20" },
{ id: 3, name: "이영희", email: "lee@test.com", status: "active", date: "2024-03-10" },
{ id: 4, name: "박민수", email: "park@test.com", status: "pending", date: "2024-04-05" },
];
const sampleHierarchyData: HierarchyNode[] = [
{
id: "1",
label: "본사",
children: [
{
id: "1-1",
label: "영업부",
children: [
{ id: "1-1-1", label: "영업1팀" },
{ id: "1-1-2", label: "영업2팀" },
],
},
{
id: "1-2",
label: "개발부",
children: [
{ id: "1-2-1", label: "프론트엔드팀" },
{ id: "1-2-2", label: "백엔드팀" },
],
},
],
},
];
return (
{/* 헤더 */}
{onBack && (
뒤로
)}
V2 컴포넌트 테스트
10개의 통합 컴포넌트를 테스트합니다
Phase 1-3 완료
{/* 탭 컨텐츠 */}
조건부
Input
Select
Date
List
Layout
Group
Media
Biz
Hierarchy
{/* 조건부 동작 데모 탭 */}
{/* V2Input 탭 */}
V2Input
통합 입력 컴포넌트 - text, number, password, slider, color, button
{/* Text Input */}
setInputValues({ ...inputValues, text: String(v) })}
/>
{/* Number Input */}
setInputValues({ ...inputValues, number: Number(v) })}
/>
{/* Password Input */}
setInputValues({ ...inputValues, password: String(v) })}
/>
{/* Slider Input */}
setInputValues({ ...inputValues, slider: Number(v) })}
/>
{/* Color Input */}
setInputValues({ ...inputValues, color: String(v) })}
/>
{/* Button */}
현재 값:
{JSON.stringify(inputValues, null, 2)}
{/* V2Select 탭 */}
V2Select
통합 선택 컴포넌트 - dropdown, radio, check, tag, toggle, swap
{/* Dropdown */}
setSelectValues({ ...selectValues, dropdown: String(v) })}
/>
{/* Radio */}
setSelectValues({ ...selectValues, radio: String(v) })}
/>
{/* Checkbox */}
setSelectValues({ ...selectValues, check: v as string[] })}
/>
{/* Tag */}
setSelectValues({ ...selectValues, tag: v as string[] })}
/>
{/* Toggle */}
setSelectValues({ ...selectValues, toggle: String(v) })}
/>
현재 값:
{JSON.stringify(selectValues, null, 2)}
{/* V2Date 탭 */}
V2Date
통합 날짜/시간 컴포넌트 - date, time, datetime, range
{/* Date */}
setDateValues({ ...dateValues, date: String(v) })}
/>
{/* Time */}
setDateValues({ ...dateValues, time: String(v) })}
/>
{/* DateTime */}
setDateValues({ ...dateValues, datetime: String(v) })}
/>
{/* Range */}
setDateValues({ ...dateValues, range: v as [string, string] })}
/>
현재 값:
{JSON.stringify(dateValues, null, 2)}
{/* V2List 탭 */}
V2List
통합 리스트 컴포넌트 - table, card, list
console.log("Row clicked:", row)}
/>
{/* V2Layout 탭 */}
V2Layout
통합 레이아웃 컴포넌트 - 12컬럼 그리드 시스템, split, flex
{/* 12컬럼 그리드 시스템 */}
12컬럼 그리드 시스템
shadcn/Tailwind 표준 12컬럼 시스템. 자식 요소에 col-span-* 클래스로 너비 조절
{/* 12컬럼 전체 보기 */}
{Array.from({ length: 12 }).map((_, i) => (
{i + 1}
))}
{/* col-span 예시 */}
col-span 활용 예시
col-span-12 (전체)
col-span-6 (절반)
col-span-6 (절반)
col-span-4 (1/3)
col-span-4 (1/3)
col-span-4 (1/3)
col-span-3 (1/4)
col-span-3 (1/4)
col-span-3 (1/4)
col-span-3 (1/4)
col-span-8 (2/3)
col-span-4 (1/3)
{/* Split Layout */}
Split Layout (리사이즈 가능)
왼쪽 패널
오른쪽 패널
{/* V2Group 탭 */}
V2Group
통합 그룹 컴포넌트 - tabs, accordion, section, card-section
{/* Tabs */}
탭 1 내용 },
{ id: "tab2", title: "탭 2", content:
탭 2 내용
},
{ id: "tab3", title: "탭 3", content:
탭 3 내용
},
],
}}
/>
{/* Accordion */}
이 내용은 접었다 펼 수 있습니다.
{/* Card Section */}
카드 스타일 섹션 내용입니다.
{/* V2Media 탭 */}
V2Media
통합 미디어 컴포넌트 - file, image, video, audio
{/* File Upload */}
{/* Image Upload */}
{/* V2Biz 탭 */}
V2Biz
통합 비즈니스 컴포넌트 - numbering, category, flow 등 (플레이스홀더)
{/* V2Hierarchy 탭 */}
V2Hierarchy
통합 계층 구조 컴포넌트 - tree, org, bom, cascading
{/* Tree View */}
트리 뷰
{/* Cascading Dropdowns */}
연쇄 드롭다운
);
}
export default V2ComponentsDemo;
// ===== 조건부 동작 데모 컴포넌트 =====
/**
* 조건부 동작을 시연하는 데모 컴포넌트
*
* 시나리오:
* 1. 계약 유형 선택 → 유형별 다른 필드 표시
* 2. VIP 여부 체크 → VIP 전용 필드 활성화
* 3. 국가 선택 → 해당 국가의 도시만 표시 (연쇄)
*/
function ConditionalDemo() {
return (
);
}
function ConditionalDemoContent() {
const { formData, setValue, evaluateCondition } = useV2Form();
// 국가별 도시 데이터
const cityOptions: Record> = {
korea: [
{ value: "seoul", label: "서울" },
{ value: "busan", label: "부산" },
{ value: "incheon", label: "인천" },
{ value: "daegu", label: "대구" },
],
japan: [
{ value: "tokyo", label: "도쿄" },
{ value: "osaka", label: "오사카" },
{ value: "kyoto", label: "교토" },
],
usa: [
{ value: "newyork", label: "뉴욕" },
{ value: "la", label: "로스앤젤레스" },
{ value: "chicago", label: "시카고" },
],
};
// 현재 선택된 국가의 도시 옵션
const currentCountry = formData.country as string;
const availableCities = currentCountry ? cityOptions[currentCountry] || [] : [];
// 조건부 설정
const showB2BFields: ConditionalConfig = {
enabled: true,
field: "contractType",
operator: "=",
value: "b2b",
action: "show",
};
const showB2CFields: ConditionalConfig = {
enabled: true,
field: "contractType",
operator: "=",
value: "b2c",
action: "show",
};
const showDiscountField: ConditionalConfig = {
enabled: true,
field: "isVip",
operator: "=",
value: true,
action: "show",
};
// 조건 평가
const b2bState = evaluateCondition("b2bFields", showB2BFields);
const b2cState = evaluateCondition("b2cFields", showB2CFields);
const discountState = evaluateCondition("discountRate", showDiscountField);
return (
{/* 시나리오 1: 계약 유형에 따른 필드 표시 */}
시나리오 1: 계약 유형별 필드 표시
계약 유형을 선택하면 해당 유형에 맞는 입력 필드가 나타납니다
{/* 계약 유형 선택 */}
setValue("contractType", v)}
/>
{/* B2B 전용 필드 */}
{b2bState.visible && (
B2B 전용 입력 필드
setValue("companyName", v)}
/>
setValue("employeeCount", v)}
/>
)}
{/* B2C 전용 필드 */}
{b2cState.visible && (
B2C 전용 입력 필드
setValue("customerName", v)}
/>
setValue("phone", v)}
/>
)}
{/* 시나리오 2: VIP 여부에 따른 필드 활성화 */}
시나리오 2: 조건부 필드 활성화
VIP 고객 체크 시 할인율 입력 필드가 나타납니다
setValue("isVip", v === "true")}
/>
{/* VIP 전용 할인율 필드 */}
{discountState.visible && (
VIP 전용 혜택 설정
setValue("discountRate", v)}
/>
현재 할인율: {formData.discountRate || 0}%
)}
{/* 시나리오 3: 연쇄 드롭다운 (국가 → 도시) */}
시나리오 3: 연쇄 드롭다운
국가를 선택하면 해당 국가의 도시만 표시됩니다
{/* 국가 선택 */}
{
setValue("country", v);
setValue("city", ""); // 국가 변경 시 도시 초기화
}}
/>
{/* 도시 선택 (국가에 따라 옵션 변경) */}
setValue("city", v)}
/>
{!currentCountry && 국가를 먼저 선택해주세요
}
{/* 조건부 설정 UI 데모 */}
{/* 현재 폼 데이터 표시 (하단으로 이동) */}
현재 폼 데이터
실시간으로 변경되는 폼 데이터를 확인합니다
{JSON.stringify(formData, null, 2)}
);
}
/**
* 조건부 설정 UI 패널 데모
*
* 비개발자도 UI로 조건부 설정을 할 수 있음을 보여주는 데모
*/
function ConditionalConfigUIDemo({ formData }: { formData: Record }) {
const [demoConfig, setDemoConfig] = useState(undefined);
const { evaluateCondition } = useV2Form();
// 데모용 필드 목록 (현재 폼의 필드들)
const availableFields = [
{
id: "contractType",
label: "계약 유형",
type: "select",
options: [
{ value: "b2b", label: "B2B" },
{ value: "b2c", label: "B2C" },
],
},
{ id: "isVip", label: "VIP 여부", type: "checkbox" },
{
id: "country",
label: "국가",
type: "select",
options: [
{ value: "korea", label: "대한민국" },
{ value: "japan", label: "일본" },
{ value: "usa", label: "미국" },
],
},
{ id: "discountRate", label: "할인율", type: "number" },
{ id: "employeeCount", label: "직원 수", type: "number" },
];
// 현재 설정으로 조건 평가
const conditionResult = demoConfig ? evaluateCondition("demoField", demoConfig) : { visible: true, disabled: false };
return (
조건부 설정 UI 데모
화면관리에서 비개발자도 이 UI로 조건부 표시를 설정할 수 있습니다
{/* 왼쪽: 설정 UI */}
{/* 오른쪽: 미리보기 */}
적용 결과 미리보기
{/* 조건 평가 결과 */}
현재 조건 평가 결과:
{conditionResult.visible ? "표시됨" : "숨겨짐"}
{conditionResult.disabled ? "비활성화" : "활성화"}
{/* 대상 필드 미리보기 */}
대상 필드:
{conditionResult.visible ? (
) : (
(조건에 의해 숨겨진 필드)
)}
{/* 생성된 JSON */}
생성된 설정 JSON:
{demoConfig ? JSON.stringify(demoConfig, null, 2) : "(설정 없음)"}
{/* 안내 메시지 */}
위의 "조건부 표시" 스위치를 켜고 설정을 변경해보세요. 위 시나리오들의 필드 값을 변경하면 조건 평가 결과가
실시간으로 바뀝니다.
);
}