diff --git a/frontend/components/screen/InteractiveScreenViewer.tsx b/frontend/components/screen/InteractiveScreenViewer.tsx index fe041a70..f6912b0a 100644 --- a/frontend/components/screen/InteractiveScreenViewer.tsx +++ b/frontend/components/screen/InteractiveScreenViewer.tsx @@ -11,7 +11,20 @@ import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover import { CalendarIcon } from "lucide-react"; import { format } from "date-fns"; import { ko } from "date-fns/locale"; -import { ComponentData } from "@/types/screen"; +import { + ComponentData, + WidgetComponent, + TextTypeConfig, + NumberTypeConfig, + DateTypeConfig, + SelectTypeConfig, + RadioTypeConfig, + CheckboxTypeConfig, + TextareaTypeConfig, + FileTypeConfig, + CodeTypeConfig, + EntityTypeConfig, +} from "@/types/screen"; interface InteractiveScreenViewerProps { component: ComponentData; @@ -78,192 +91,553 @@ export const InteractiveScreenViewer: React.FC = ( switch (widgetType) { case "text": case "email": - case "tel": + case "tel": { + const widget = comp as WidgetComponent; + const config = widget.webTypeConfig as TextTypeConfig | undefined; + + console.log("๐Ÿ“ InteractiveScreenViewer - Text ์œ„์ ฏ:", { + componentId: widget.id, + widgetType: widget.widgetType, + config, + appliedSettings: { + format: config?.format, + minLength: config?.minLength, + maxLength: config?.maxLength, + pattern: config?.pattern, + placeholder: config?.placeholder, + }, + }); + + // ํ˜•์‹๋ณ„ ํŒจํ„ด ์ƒ์„ฑ + const getPatternByFormat = (format: string) => { + switch (format) { + case "korean": + return "[๊ฐ€-ํžฃ\\s]*"; + case "english": + return "[a-zA-Z\\s]*"; + case "alphanumeric": + return "[a-zA-Z0-9]*"; + case "numeric": + return "[0-9]*"; + case "email": + return "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}"; + case "phone": + return "\\d{3}-\\d{4}-\\d{4}"; + case "url": + return "https?://[\\w\\-]+(\\.[\\w\\-]+)+([\\w\\-\\.,@?^=%&:/~\\+#]*[\\w\\-\\@?^=%&/~\\+#])?"; + default: + return config?.pattern || undefined; + } + }; + + // ์ž…๋ ฅ ๊ฒ€์ฆ ํ•จ์ˆ˜ + const handleInputChange = (e: React.ChangeEvent) => { + const value = e.target.value; + + // ํ˜•์‹๋ณ„ ์‹ค์‹œ๊ฐ„ ๊ฒ€์ฆ + if (config?.format && config.format !== "none") { + const pattern = getPatternByFormat(config.format); + if (pattern) { + const regex = new RegExp(`^${pattern}$`); + if (value && !regex.test(value)) { + return; // ์œ ํšจํ•˜์ง€ ์•Š์€ ์ž…๋ ฅ ์ฐจ๋‹จ + } + } + } + + // ๊ธธ์ด ์ œํ•œ ๊ฒ€์ฆ + if (config?.maxLength && value.length > config.maxLength) { + return; // ์ตœ๋Œ€ ๊ธธ์ด ์ดˆ๊ณผ ์ฐจ๋‹จ + } + + updateFormData(fieldName, value); + }; + + const finalPlaceholder = config?.placeholder || placeholder || "์ž…๋ ฅํ•˜์„ธ์š”..."; + const inputType = widgetType === "email" ? "email" : widgetType === "tel" ? "tel" : "text"; + return applyStyles( updateFormData(fieldName, e.target.value)} + onChange={handleInputChange} disabled={readonly} required={required} + minLength={config?.minLength} + maxLength={config?.maxLength} + pattern={getPatternByFormat(config?.format || "none")} className="h-full w-full" />, ); + } case "number": - case "decimal": + case "decimal": { + const widget = comp as WidgetComponent; + const config = widget.webTypeConfig as NumberTypeConfig | undefined; + + console.log("๐Ÿ”ข InteractiveScreenViewer - Number ์œ„์ ฏ:", { + componentId: widget.id, + widgetType: widget.widgetType, + config, + appliedSettings: { + format: config?.format, + min: config?.min, + max: config?.max, + step: config?.step, + decimalPlaces: config?.decimalPlaces, + thousandSeparator: config?.thousandSeparator, + prefix: config?.prefix, + suffix: config?.suffix, + }, + }); + + const step = config?.step || (widgetType === "decimal" ? 0.01 : 1); + const finalPlaceholder = config?.placeholder || placeholder || "์ˆซ์ž๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”..."; + return applyStyles( updateFormData(fieldName, e.target.valueAsNumber || 0)} disabled={readonly} required={required} + min={config?.min} + max={config?.max} + step={step} className="h-full w-full" - step={widgetType === "decimal" ? "0.01" : "1"} />, ); + } case "textarea": - case "text_area": + case "text_area": { + const widget = comp as WidgetComponent; + const config = widget.webTypeConfig as TextareaTypeConfig | undefined; + + console.log("๐Ÿ“„ InteractiveScreenViewer - Textarea ์œ„์ ฏ:", { + componentId: widget.id, + widgetType: widget.widgetType, + config, + appliedSettings: { + rows: config?.rows, + maxLength: config?.maxLength, + minLength: config?.minLength, + placeholder: config?.placeholder, + defaultValue: config?.defaultValue, + resizable: config?.resizable, + wordWrap: config?.wordWrap, + }, + }); + + const finalPlaceholder = config?.placeholder || placeholder || "๋‚ด์šฉ์„ ์ž…๋ ฅํ•˜์„ธ์š”..."; + const rows = config?.rows || 3; + return applyStyles(