ERP-node/frontend/lib/meta-components/Search/SearchRenderer.tsx

177 lines
4.9 KiB
TypeScript

"use client";
/**
* Search 메타 컴포넌트 렌더러
* - config.fields 배열로 검색 필드들을 가로로 배치
* - "검색" 버튼 클릭 시 v2EventBus로 TABLE_REFRESH + 필터 전달
* - "초기화" 버튼으로 모든 필터 클리어
* - 디자인 모드: 비활성 상태로 표시
*/
import React, { useState } from "react";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Search, X } from "lucide-react";
import { v2EventBus } from "@/lib/v2-core/events/EventBus";
import { V2_EVENTS } from "@/lib/v2-core/events/types";
interface SearchField {
columnName: string;
label: string;
searchType?: "text" | "select" | "date";
options?: { value: string; label: string }[];
}
interface SearchRendererProps {
id: string;
config: {
fields: SearchField[];
targetTableId?: string; // 연결된 DataView ID (옵션)
targetTableName?: string; // 🔍 대상 테이블명 (필터 식별용)
};
isDesignMode?: boolean;
disabled?: boolean;
className?: string;
}
export function SearchRenderer({
id,
config,
isDesignMode = false,
disabled = false,
className,
}: SearchRendererProps) {
const { fields = [] } = config;
const [filters, setFilters] = useState<Record<string, any>>({});
// 검색 실행
const handleSearch = () => {
if (isDesignMode || disabled) return;
// 🔍 v2EventBus로 TABLE_REFRESH 이벤트 발행
v2EventBus.emitSync(V2_EVENTS.TABLE_REFRESH, {
filters,
sourceId: id,
targetId: config.targetTableId,
tableName: config.targetTableName, // 대상 테이블명 전달
});
};
// 필터 초기화
const handleReset = () => {
if (isDesignMode || disabled) return;
setFilters({});
// 🔍 빈 필터로 새로고침
v2EventBus.emitSync(V2_EVENTS.TABLE_REFRESH, {
filters: {},
sourceId: id,
targetId: config.targetTableId,
tableName: config.targetTableName, // 대상 테이블명 전달
});
};
// 필드 값 변경
const handleFieldChange = (columnName: string, value: any) => {
setFilters((prev) => ({ ...prev, [columnName]: value }));
};
// Enter 키 처리
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === "Enter") {
handleSearch();
}
};
if (fields.length === 0) {
return (
<div
className={cn(
"flex items-center justify-center rounded-md border border-dashed p-4 text-sm text-muted-foreground",
className
)}
>
</div>
);
}
return (
<div className={cn("flex flex-wrap items-end gap-2", className)}>
{fields.map((field) => (
<div key={field.columnName} className="flex flex-col gap-1">
<Label className="text-xs sm:text-sm">{field.label}</Label>
{field.searchType === "select" && field.options ? (
<Select
value={filters[field.columnName] || ""}
onValueChange={(value) => handleFieldChange(field.columnName, value)}
disabled={isDesignMode || disabled}
>
<SelectTrigger className="h-8 w-32 sm:h-10 sm:w-40">
<SelectValue placeholder="선택" />
</SelectTrigger>
<SelectContent>
{field.options.map((opt) => (
<SelectItem key={opt.value} value={opt.value}>
{opt.label}
</SelectItem>
))}
</SelectContent>
</Select>
) : (
<Input
type={field.searchType === "date" ? "date" : "text"}
placeholder={field.label}
value={filters[field.columnName] || ""}
onChange={(e) => handleFieldChange(field.columnName, e.target.value)}
onKeyDown={handleKeyDown}
disabled={isDesignMode || disabled}
className="h-8 w-32 sm:h-10 sm:w-40"
/>
)}
</div>
))}
{/* 검색 버튼 */}
<Button
onClick={handleSearch}
disabled={isDesignMode || disabled}
size="default"
className="h-8 sm:h-10"
>
<Search className="mr-2 h-4 w-4" />
</Button>
{/* 초기화 버튼 */}
<Button
onClick={handleReset}
disabled={isDesignMode || disabled}
variant="outline"
size="default"
className="h-8 sm:h-10"
>
<X className="mr-2 h-4 w-4" />
</Button>
{/* 디자인 모드 표시 */}
{isDesignMode && (
<span className="ml-2 text-xs text-muted-foreground">
( 모드: 비활성)
</span>
)}
</div>
);
}