ERP-node/frontend/components/screen/WidgetFactory.tsx

210 lines
8.0 KiB
TypeScript
Raw Normal View History

2025-09-01 11:48:12 +09:00
"use client";
import { WidgetComponent } from "@/types/screen";
import InputWidget from "./widgets/InputWidget";
import SelectWidget from "./widgets/SelectWidget";
import TextareaWidget from "./widgets/TextareaWidget";
import { Calendar, CheckSquare, Radio, FileText, Hash, Database } from "lucide-react";
interface WidgetFactoryProps {
widget: WidgetComponent;
value?: string;
onChange?: (value: string) => void;
className?: string;
}
export default function WidgetFactory({ widget, value, onChange, className }: WidgetFactoryProps) {
// 웹 타입에 따라 적절한 컴포넌트 렌더링
switch (widget.widgetType) {
case "text":
return <InputWidget widget={widget} value={value} onChange={onChange} className={className} />;
case "number":
return <InputWidget widget={widget} value={value} onChange={onChange} className={className} />;
case "date":
return (
<div className={`space-y-2 ${className}`}>
{widget.label && (
<label htmlFor={widget.id} className="text-sm font-medium">
{widget.label}
{widget.required && <span className="ml-1 text-red-500">*</span>}
</label>
)}
<div className="relative">
<Calendar className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-gray-400" />
<input
id={widget.id}
type="date"
placeholder={widget.placeholder || "날짜를 선택하세요"}
value={value || ""}
onChange={(e) => onChange?.(e.target.value)}
required={widget.required}
readOnly={widget.readonly}
className="w-full rounded-md border border-gray-300 py-2 pr-3 pl-10 text-sm focus:border-primary focus:ring-1 focus:ring-blue-500 focus:outline-none"
2025-09-01 11:48:12 +09:00
/>
</div>
</div>
);
case "code":
return (
<div className={`space-y-2 ${className}`}>
{widget.label && (
<label htmlFor={widget.id} className="text-sm font-medium">
{widget.label}
{widget.required && <span className="ml-1 text-red-500">*</span>}
</label>
)}
<div className="relative">
<Hash className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-gray-400" />
<input
id={widget.id}
type="text"
placeholder={widget.placeholder || "코드를 입력하세요"}
value={value || ""}
onChange={(e) => onChange?.(e.target.value)}
required={widget.required}
readOnly={widget.readonly}
className="w-full rounded-md border border-gray-300 py-2 pr-3 pl-10 font-mono text-sm focus:border-primary focus:ring-1 focus:ring-blue-500 focus:outline-none"
2025-09-01 11:48:12 +09:00
/>
</div>
</div>
);
case "entity":
return (
<div className={`space-y-2 ${className}`}>
{widget.label && (
<label htmlFor={widget.id} className="text-sm font-medium">
{widget.label}
{widget.required && <span className="ml-1 text-red-500">*</span>}
</label>
)}
<div className="relative">
<Database className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-gray-400" />
<input
id={widget.id}
type="text"
placeholder={widget.placeholder || "엔티티를 선택하세요"}
value={value || ""}
onChange={(e) => onChange?.(e.target.value)}
required={widget.required}
readOnly={widget.readonly}
className="w-full rounded-md border border-gray-300 py-2 pr-3 pl-10 text-sm focus:border-primary focus:ring-1 focus:ring-blue-500 focus:outline-none"
2025-09-01 11:48:12 +09:00
/>
</div>
</div>
);
case "file":
return (
<div className={`space-y-2 ${className}`}>
{widget.label && (
<label htmlFor={widget.id} className="text-sm font-medium">
{widget.label}
{widget.required && <span className="ml-1 text-red-500">*</span>}
</label>
)}
<div className="relative">
<FileText className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-gray-400" />
<input
id={widget.id}
type="file"
onChange={(e) => onChange?.(e.target.files?.[0]?.name || "")}
required={widget.required}
disabled={widget.readonly}
className="w-full rounded-md border border-gray-300 py-2 pr-3 pl-10 text-sm file:mr-4 file:rounded-md file:border-0 file:bg-accent file:px-4 file:py-1 file:text-sm file:font-medium file:text-blue-700 hover:file:bg-primary/20 focus:border-primary focus:ring-1 focus:ring-blue-500 focus:outline-none"
2025-09-01 11:48:12 +09:00
/>
</div>
</div>
);
case "select":
return <SelectWidget widget={widget} value={value} onChange={onChange} className={className} />;
case "checkbox":
return (
<div className={`space-y-2 ${className}`}>
<div className="flex items-center space-x-2">
<CheckSquare className="h-4 w-4 text-gray-400" />
<input
id={widget.id}
type="checkbox"
checked={value === "true"}
onChange={(e) => onChange?.(e.target.checked ? "true" : "false")}
required={widget.required}
disabled={widget.readonly}
className="h-4 w-4 rounded border-gray-300 text-primary focus:ring-blue-500"
2025-09-01 11:48:12 +09:00
/>
{widget.label && (
<label htmlFor={widget.id} className="text-sm font-medium">
{widget.label}
{widget.required && <span className="ml-1 text-red-500">*</span>}
</label>
)}
</div>
</div>
);
case "radio":
return (
<div className={`space-y-2 ${className}`}>
{widget.label && (
<label className="text-sm font-medium">
{widget.label}
{widget.required && <span className="ml-1 text-red-500">*</span>}
</label>
)}
<div className="space-y-2">
<div className="flex items-center space-x-2">
<Radio className="h-4 w-4 text-gray-400" />
<input
id={`${widget.id}-yes`}
name={widget.id}
type="radio"
value="yes"
checked={value === "yes"}
onChange={(e) => onChange?.(e.target.value)}
required={widget.required}
disabled={widget.readonly}
className="h-4 w-4 border-gray-300 text-primary focus:ring-blue-500"
2025-09-01 11:48:12 +09:00
/>
<label htmlFor={`${widget.id}-yes`} className="text-sm">
</label>
</div>
<div className="flex items-center space-x-2">
<Radio className="h-4 w-4 text-gray-400" />
<input
id={`${widget.id}-no`}
name={widget.id}
type="radio"
value="no"
checked={value === "no"}
onChange={(e) => onChange?.(e.target.value)}
required={widget.required}
disabled={widget.readonly}
className="h-4 w-4 border-gray-300 text-primary focus:ring-blue-500"
2025-09-01 11:48:12 +09:00
/>
<label htmlFor={`${widget.id}-no`} className="text-sm">
</label>
</div>
</div>
</div>
);
case "textarea":
return <TextareaWidget widget={widget} value={value} onChange={onChange} className={className} />;
default:
return (
<div className={`rounded border border-red-300 bg-destructive/10 p-4 text-destructive ${className}`}>
2025-09-01 11:48:12 +09:00
<p className="text-sm font-medium"> </p>
<p className="text-xs text-red-500">: {widget.widgetType}</p>
</div>
);
}
}