"use client"; import React, { useState, useRef, useEffect, useMemo } from "react"; import { ComponentRendererProps } from "@/types/component"; import { ButtonPrimaryConfig } from "./types"; import { ButtonActionExecutor, ButtonActionContext, ButtonActionType, DEFAULT_BUTTON_ACTIONS, } from "@/lib/utils/buttonActions"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; import { toast } from "sonner"; import { filterDOMProps } from "@/lib/utils/domPropsFilter"; import { useCurrentFlowStep } from "@/stores/flowStepStore"; import { useScreenPreview } from "@/contexts/ScreenPreviewContext"; import { useScreenContextOptional } from "@/contexts/ScreenContext"; import { useSplitPanelContext, SplitPanelPosition } from "@/contexts/SplitPanelContext"; import { applyMappingRules } from "@/lib/utils/dataMapping"; import { apiClient } from "@/lib/api/client"; export interface ButtonPrimaryComponentProps extends ComponentRendererProps { config?: ButtonPrimaryConfig; // ์ถ”๊ฐ€ props screenId?: number; tableName?: string; userId?: string; // ๐Ÿ†• ํ˜„์žฌ ์‚ฌ์šฉ์ž ID userName?: string; // ๐Ÿ†• ํ˜„์žฌ ์‚ฌ์šฉ์ž ์ด๋ฆ„ companyCode?: string; // ๐Ÿ†• ํ˜„์žฌ ์‚ฌ์šฉ์ž์˜ ํšŒ์‚ฌ ์ฝ”๋“œ onRefresh?: () => void; onClose?: () => void; onFlowRefresh?: () => void; onSave?: () => Promise; // ๐Ÿ†• EditModal์˜ handleSave ์ฝœ๋ฐฑ // ํผ ๋ฐ์ดํ„ฐ ๊ด€๋ จ originalData?: Record; // ๋ถ€๋ถ„ ์—…๋ฐ์ดํŠธ์šฉ ์›๋ณธ ๋ฐ์ดํ„ฐ // ํ…Œ์ด๋ธ” ์„ ํƒ๋œ ํ–‰ ์ •๋ณด (๋‹ค์ค‘ ์„ ํƒ ์•ก์…˜์šฉ) selectedRows?: any[]; selectedRowsData?: any[]; // ํ…Œ์ด๋ธ” ์ •๋ ฌ ์ •๋ณด (์—‘์…€ ๋‹ค์šด๋กœ๋“œ์šฉ) sortBy?: string; sortOrder?: "asc" | "desc"; columnOrder?: string[]; tableDisplayData?: any[]; // ํ™”๋ฉด์— ํ‘œ์‹œ๋œ ๋ฐ์ดํ„ฐ (์ปฌ๋Ÿผ ์ˆœ์„œ ํฌํ•จ) // ํ”Œ๋กœ์šฐ ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ ์ •๋ณด (ํ”Œ๋กœ์šฐ ์œ„์ ฏ ์„ ํƒ ์•ก์…˜์šฉ) flowSelectedData?: any[]; flowSelectedStepId?: number | null; // ๐Ÿ†• ๊ฐ™์€ ํ™”๋ฉด์˜ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ (TableList ์ž๋™ ๊ฐ์ง€์šฉ) allComponents?: any[]; // ๐Ÿ†• ๋ถ€๋ชจ์ฐฝ์—์„œ ์ „๋‹ฌ๋œ ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ (๋ชจ๋‹ฌ์—์„œ ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ์šฉ) groupedData?: Record[]; } /** * ButtonPrimary ์ปดํฌ๋„ŒํŠธ * button-primary ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค */ export const ButtonPrimaryComponent: React.FC = ({ component, isDesignMode = false, isSelected = false, isInteractive = false, onClick, onDragStart, onDragEnd, config, className, style, formData, originalData, onFormDataChange, screenId, tableName, userId, // ๐Ÿ†• ์‚ฌ์šฉ์ž ID userName, // ๐Ÿ†• ์‚ฌ์šฉ์ž ์ด๋ฆ„ companyCode, // ๐Ÿ†• ํšŒ์‚ฌ ์ฝ”๋“œ onRefresh, onClose, onFlowRefresh, onSave, // ๐Ÿ†• EditModal์˜ handleSave ์ฝœ๋ฐฑ sortBy, // ๐Ÿ†• ์ •๋ ฌ ์ปฌ๋Ÿผ sortOrder, // ๐Ÿ†• ์ •๋ ฌ ๋ฐฉํ–ฅ columnOrder, // ๐Ÿ†• ์ปฌ๋Ÿผ ์ˆœ์„œ tableDisplayData, // ๐Ÿ†• ํ™”๋ฉด์— ํ‘œ์‹œ๋œ ๋ฐ์ดํ„ฐ selectedRows, selectedRowsData, flowSelectedData, flowSelectedStepId, allComponents, // ๐Ÿ†• ๊ฐ™์€ ํ™”๋ฉด์˜ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ groupedData, // ๐Ÿ†• ๋ถ€๋ชจ์ฐฝ์—์„œ ์ „๋‹ฌ๋œ ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ ...props }) => { const { isPreviewMode } = useScreenPreview(); // ํ”„๋ฆฌ๋ทฐ ๋ชจ๋“œ ํ™•์ธ const screenContext = useScreenContextOptional(); // ํ™”๋ฉด ์ปจํ…์ŠคํŠธ const splitPanelContext = useSplitPanelContext(); // ๋ถ„ํ•  ํŒจ๋„ ์ปจํ…์ŠคํŠธ // ๐Ÿ†• ScreenContext์—์„œ splitPanelPosition ๊ฐ€์ ธ์˜ค๊ธฐ (์ค‘์ฒฉ ํ™”๋ฉด์—์„œ๋„ ์ž‘๋™) const splitPanelPosition = screenContext?.splitPanelPosition; // ๐Ÿ†• tableName์ด props๋กœ ์ „๋‹ฌ๋˜์ง€ ์•Š์œผ๋ฉด ScreenContext์—์„œ ๊ฐ€์ ธ์˜ค๊ธฐ const effectiveTableName = tableName || screenContext?.tableName; const effectiveScreenId = screenId || screenContext?.screenId; // ๐Ÿ†• props์—์„œ onSave ์ถ”์ถœ (๋ช…์‹œ์ ์œผ๋กœ ์„ ์–ธ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ...props์—์„œ ์ถ”์ถœ) const propsOnSave = (props as any).onSave as (() => Promise) | undefined; const finalOnSave = onSave || propsOnSave; // ๐Ÿ†• ํ”Œ๋กœ์šฐ ๋‹จ๊ณ„๋ณ„ ํ‘œ์‹œ ์ œ์–ด const flowConfig = (component as any).webTypeConfig?.flowVisibilityConfig; const currentStep = useCurrentFlowStep(flowConfig?.targetFlowComponentId); // ๐Ÿ†• ๋ฒ„ํŠผ ํ‘œ์‹œ ์—ฌ๋ถ€ ๊ณ„์‚ฐ const shouldShowButton = useMemo(() => { // ํ”Œ๋กœ์šฐ ์ œ์–ด ๋น„ํ™œ์„ฑํ™” ์‹œ ํ•ญ์ƒ ํ‘œ์‹œ if (!flowConfig?.enabled) { return true; } // ํ”Œ๋กœ์šฐ ๋‹จ๊ณ„๊ฐ€ ์„ ํƒ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ์ฒ˜๋ฆฌ if (currentStep === null) { // ๐Ÿ”ง ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ ๋ชจ๋“œ์ผ ๋•Œ๋Š” ๋‹จ๊ณ„ ๋ฏธ์„ ํƒ ์‹œ ์ˆจ๊น€ if (flowConfig.mode === "whitelist") { return false; } // ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ๋‚˜ all ๋ชจ๋“œ๋Š” ํ‘œ์‹œ return true; } const { mode, visibleSteps = [], hiddenSteps = [] } = flowConfig; let result = true; if (mode === "whitelist") { result = visibleSteps.includes(currentStep); } else if (mode === "blacklist") { result = !hiddenSteps.includes(currentStep); } else if (mode === "all") { result = true; } return result; }, [flowConfig, currentStep, component.id, component.label]); // ๐Ÿ†• ์šดํ–‰์•Œ๋ฆผ ๋ฒ„ํŠผ ์กฐ๊ฑด๋ถ€ ๋น„ํ™œ์„ฑํ™” (์ถœ๋ฐœ์ง€/๋„์ฐฉ์ง€ ํ•„์ˆ˜, ์ƒํƒœ ์ฒดํฌ) // ์ƒํƒœ๋Š” API๋กœ ์กฐํšŒ (formData์— ์—†๋Š” ๊ฒฝ์šฐ) const [vehicleStatus, setVehicleStatus] = useState(null); const [statusLoading, setStatusLoading] = useState(false); // ์ƒํƒœ ์กฐํšŒ (operation_control + enableOnStatusCheck์ผ ๋•Œ) const actionConfig = component.componentConfig?.action; const shouldFetchStatus = actionConfig?.type === "operation_control" && actionConfig?.enableOnStatusCheck && userId; const statusTableName = actionConfig?.statusCheckTableName || "vehicles"; const statusKeyField = actionConfig?.statusCheckKeyField || "user_id"; const statusFieldName = actionConfig?.statusCheckField || "status"; useEffect(() => { if (!shouldFetchStatus) return; let isMounted = true; const fetchStatus = async () => { if (!isMounted) return; try { const response = await apiClient.post(`/table-management/tables/${statusTableName}/data`, { page: 1, size: 1, search: { [statusKeyField]: userId }, autoFilter: true, }); if (!isMounted) return; const rows = response.data?.data?.data || response.data?.data?.rows || response.data?.rows || []; const firstRow = Array.isArray(rows) ? rows[0] : null; if (response.data?.success && firstRow) { const newStatus = firstRow[statusFieldName]; if (newStatus !== vehicleStatus) { // console.log("๐Ÿ”„ [ButtonPrimary] ์ƒํƒœ ๋ณ€๊ฒฝ ๊ฐ์ง€:", { ์ด์ „: vehicleStatus, ํ˜„์žฌ: newStatus, buttonLabel: component.label }); } setVehicleStatus(newStatus); } else { setVehicleStatus(null); } } catch (error: any) { // console.error("โŒ [ButtonPrimary] ์ƒํƒœ ์กฐํšŒ ์˜ค๋ฅ˜:", error?.message); if (isMounted) setVehicleStatus(null); } finally { if (isMounted) setStatusLoading(false); } }; // ์ฆ‰์‹œ ์‹คํ–‰ setStatusLoading(true); fetchStatus(); // 2์ดˆ๋งˆ๋‹ค ๊ฐฑ์‹  const interval = setInterval(fetchStatus, 2000); return () => { isMounted = false; clearInterval(interval); }; }, [shouldFetchStatus, statusTableName, statusKeyField, statusFieldName, userId, component.label]); // ๋ฒ„ํŠผ ๋น„ํ™œ์„ฑํ™” ์กฐ๊ฑด ๊ณ„์‚ฐ const isOperationButtonDisabled = useMemo(() => { const actionConfig = component.componentConfig?.action; if (actionConfig?.type !== "operation_control") return false; // 1. ์ถœ๋ฐœ์ง€/๋„์ฐฉ์ง€ ํ•„์ˆ˜ ์ฒดํฌ if (actionConfig?.requireLocationFields) { const departureField = actionConfig.trackingDepartureField || "departure"; const destinationField = actionConfig.trackingArrivalField || "destination"; const departure = formData?.[departureField]; const destination = formData?.[destinationField]; // console.log("๐Ÿ” [ButtonPrimary] ์ถœ๋ฐœ์ง€/๋„์ฐฉ์ง€ ์ฒดํฌ:", { // departureField, destinationField, departure, destination, // buttonLabel: component.label // }); if (!departure || departure === "" || !destination || destination === "") { // console.log("๐Ÿšซ [ButtonPrimary] ์ถœ๋ฐœ์ง€/๋„์ฐฉ์ง€ ๋ฏธ์„ ํƒ โ†’ ๋น„ํ™œ์„ฑํ™”:", component.label); return true; } } // 2. ์ƒํƒœ ๊ธฐ๋ฐ˜ ํ™œ์„ฑํ™” ์กฐ๊ฑด (API๋กœ ์กฐํšŒํ•œ vehicleStatus ์šฐ์„  ์‚ฌ์šฉ) if (actionConfig?.enableOnStatusCheck) { const statusField = actionConfig.statusCheckField || "status"; // API ์กฐํšŒ ๊ฒฐ๊ณผ๋ฅผ ์šฐ์„  ์‚ฌ์šฉ (์‹ค์‹œ๊ฐ„ DB ์ƒํƒœ ๋ฐ˜์˜) const currentStatus = vehicleStatus || formData?.[statusField]; const conditionType = actionConfig.statusConditionType || "enableOn"; const conditionValues = (actionConfig.statusConditionValues || "") .split(",") .map((v: string) => v.trim()) .filter((v: string) => v); // console.log("๐Ÿ” [ButtonPrimary] ์ƒํƒœ ์กฐ๊ฑด ์ฒดํฌ:", { // statusField, // formDataStatus: formData?.[statusField], // apiStatus: vehicleStatus, // currentStatus, // conditionType, // conditionValues, // buttonLabel: component.label, // }); // ์ƒํƒœ ๋กœ๋”ฉ ์ค‘์ด๋ฉด ๋น„ํ™œ์„ฑํ™” if (statusLoading) { // console.log("โณ [ButtonPrimary] ์ƒํƒœ ๋กœ๋”ฉ ์ค‘ โ†’ ๋น„ํ™œ์„ฑํ™”:", component.label); return true; } // ์ƒํƒœ๊ฐ’์ด ์—†์œผ๋ฉด โ†’ ๋น„ํ™œ์„ฑํ™” (์กฐ๊ฑด ํ™•์ธ ๋ถˆ๊ฐ€) if (!currentStatus) { // console.log("๐Ÿšซ [ButtonPrimary] ์ƒํƒœ๊ฐ’ ์—†์Œ โ†’ ๋น„ํ™œ์„ฑํ™”:", component.label); return true; } if (conditionValues.length > 0) { if (conditionType === "enableOn") { // ์ด ์ƒํƒœ์ผ ๋•Œ๋งŒ ํ™œ์„ฑํ™” if (!conditionValues.includes(currentStatus)) { // console.log(`๐Ÿšซ [ButtonPrimary] ์ƒํƒœ ${currentStatus} โˆ‰ [${conditionValues}] โ†’ ๋น„ํ™œ์„ฑํ™”:`, component.label); return true; } } else if (conditionType === "disableOn") { // ์ด ์ƒํƒœ์ผ ๋•Œ ๋น„ํ™œ์„ฑํ™” if (conditionValues.includes(currentStatus)) { // console.log(`๐Ÿšซ [ButtonPrimary] ์ƒํƒœ ${currentStatus} โˆˆ [${conditionValues}] โ†’ ๋น„ํ™œ์„ฑํ™”:`, component.label); return true; } } } } // console.log("โœ… [ButtonPrimary] ๋ฒ„ํŠผ ํ™œ์„ฑํ™”:", component.label); return false; }, [component.componentConfig?.action, formData, vehicleStatus, statusLoading, component.label]); // ๐Ÿ†• modalDataStore์—์„œ ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ ํ™•์ธ (๋ถ„ํ•  ํŒจ๋„ ๋“ฑ์—์„œ ์ €์žฅ๋จ) const [modalStoreData, setModalStoreData] = useState>({}); // modalDataStore ์ƒํƒœ ๊ตฌ๋… (์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ) useEffect(() => { const actionConfig = component.componentConfig?.action; if (!actionConfig?.requireRowSelection) return; // ๋™์  import๋กœ modalDataStore ๊ตฌ๋… let unsubscribe: (() => void) | undefined; import("@/stores/modalDataStore").then(({ useModalDataStore }) => { // ์ดˆ๊ธฐ๊ฐ’ ์„ค์ • setModalStoreData(useModalDataStore.getState().dataRegistry); // ์ƒํƒœ ๋ณ€๊ฒฝ ๊ตฌ๋… unsubscribe = useModalDataStore.subscribe((state) => { setModalStoreData(state.dataRegistry); }); }); return () => { unsubscribe?.(); }; }, [component.componentConfig?.action?.requireRowSelection]); // ๐Ÿ†• ํ–‰ ์„ ํƒ ๊ธฐ๋ฐ˜ ๋น„ํ™œ์„ฑํ™” ์กฐ๊ฑด ๊ณ„์‚ฐ const isRowSelectionDisabled = useMemo(() => { const actionConfig = component.componentConfig?.action; // requireRowSelection์ด ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด ๋น„ํ™œ์„ฑํ™”ํ•˜์ง€ ์•Š์Œ if (!actionConfig?.requireRowSelection) { return false; } const rowSelectionSource = actionConfig.rowSelectionSource || "auto"; const allowMultiRowSelection = actionConfig.allowMultiRowSelection ?? true; // ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ ํ™•์ธ let hasSelection = false; let selectionCount = 0; let selectionSource = ""; // 1. ์ž๋™ ๊ฐ์ง€ ๋ชจ๋“œ ๋˜๋Š” ํ…Œ์ด๋ธ” ๋ฆฌ์ŠคํŠธ ๋ชจ๋“œ if (rowSelectionSource === "auto" || rowSelectionSource === "tableList") { // TableList์—์„œ ์„ ํƒ๋œ ํ–‰ ํ™•์ธ (props๋กœ ์ „๋‹ฌ๋จ) if (selectedRowsData && selectedRowsData.length > 0) { hasSelection = true; selectionCount = selectedRowsData.length; selectionSource = "tableList (selectedRowsData)"; } // ๋˜๋Š” selectedRows prop ํ™•์ธ else if (selectedRows && selectedRows.length > 0) { hasSelection = true; selectionCount = selectedRows.length; selectionSource = "tableList (selectedRows)"; } } // 2. ๋ถ„ํ•  ํŒจ๋„ ์ขŒ์ธก ์„ ํƒ ๋ฐ์ดํ„ฐ ํ™•์ธ if (rowSelectionSource === "auto" || rowSelectionSource === "splitPanelLeft") { // SplitPanelContext์—์„œ ํ™•์ธ if (splitPanelContext?.selectedLeftData && Object.keys(splitPanelContext.selectedLeftData).length > 0) { if (!hasSelection) { hasSelection = true; selectionCount = 1; selectionSource = "splitPanelLeft (context)"; } } // ๐Ÿ†• modalDataStore์—์„œ๋„ ํ™•์ธ (SplitPanelLayoutComponent์—์„œ ์ €์žฅ) if (!hasSelection && Object.keys(modalStoreData).length > 0) { // modalDataStore์—์„œ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ for (const [sourceId, items] of Object.entries(modalStoreData)) { if (items && items.length > 0) { hasSelection = true; selectionCount = items.length; selectionSource = `modalDataStore (${sourceId})`; break; } } } } // 3. ํ”Œ๋กœ์šฐ ์œ„์ ฏ ์„ ํƒ ๋ฐ์ดํ„ฐ ํ™•์ธ if (rowSelectionSource === "auto" || rowSelectionSource === "flowWidget") { // ํ”Œ๋กœ์šฐ ์œ„์ ฏ ์„ ํƒ ๋ฐ์ดํ„ฐ ํ™•์ธ if (!hasSelection && flowSelectedData && flowSelectedData.length > 0) { hasSelection = true; selectionCount = flowSelectedData.length; selectionSource = "flowWidget"; } } // ๋””๋ฒ„๊น… ๋กœ๊ทธ console.log("๐Ÿ” [ButtonPrimary] ํ–‰ ์„ ํƒ ์ฒดํฌ:", component.label, { rowSelectionSource, hasSelection, selectionCount, selectionSource, hasSplitPanelContext: !!splitPanelContext, trackedSelectedLeftData: trackedSelectedLeftData, selectedRowsData: selectedRowsData?.length, selectedRows: selectedRows?.length, flowSelectedData: flowSelectedData?.length, modalStoreDataKeys: Object.keys(modalStoreData), }); // ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์œผ๋ฉด ๋น„ํ™œ์„ฑํ™” if (!hasSelection) { return true; } // ๋‹ค์ค‘ ์„ ํƒ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ, ์ •ํ™•ํžˆ 1๊ฐœ๋งŒ ์„ ํƒ๋˜์–ด์•ผ ํ•จ if (!allowMultiRowSelection && selectionCount !== 1) { return true; } return false; }, [ component.componentConfig?.action, component.label, selectedRows, selectedRowsData, splitPanelContext?.selectedLeftData, flowSelectedData, splitPanelContext, modalStoreData, ]); // ํ™•์ธ ๋‹ค์ด์–ผ๋กœ๊ทธ ์ƒํƒœ const [showConfirmDialog, setShowConfirmDialog] = useState(false); const [pendingAction, setPendingAction] = useState<{ type: ButtonActionType; config: any; context: ButtonActionContext; } | null>(null); // ํ† ์ŠคํŠธ ์ •๋ฆฌ๋ฅผ ์œ„ํ•œ ref const currentLoadingToastRef = useRef(undefined); // ์ปดํฌ๋„ŒํŠธ ์–ธ๋งˆ์šดํŠธ ์‹œ ํ† ์ŠคํŠธ ์ •๋ฆฌ useEffect(() => { return () => { if (currentLoadingToastRef.current !== undefined) { toast.dismiss(currentLoadingToastRef.current); currentLoadingToastRef.current = undefined; } }; }, []); // ์‚ญ์ œ ์•ก์…˜ ๊ฐ์ง€ ๋กœ์ง (์‹ค์ œ ํ•„๋“œ๋ช… ์‚ฌ์šฉ) const isDeleteAction = () => { const deleteKeywords = ["์‚ญ์ œ", "delete", "remove", "์ œ๊ฑฐ", "del"]; return ( component.componentConfig?.action?.type === "delete" || component.config?.action?.type === "delete" || component.webTypeConfig?.actionType === "delete" || component.text?.toLowerCase().includes("์‚ญ์ œ") || component.text?.toLowerCase().includes("delete") || component.label?.toLowerCase().includes("์‚ญ์ œ") || component.label?.toLowerCase().includes("delete") || deleteKeywords.some( (keyword) => component.config?.buttonText?.toLowerCase().includes(keyword) || component.config?.text?.toLowerCase().includes(keyword), ) ); }; // ์‚ญ์ œ ์•ก์…˜์ผ ๋•Œ ๋ผ๋ฒจ ์ƒ‰์ƒ ์ž๋™ ์„ค์ • useEffect(() => { if (isDeleteAction() && !component.style?.labelColor) { // ์‚ญ์ œ ์•ก์…˜์ด๊ณ  ๋ผ๋ฒจ ์ƒ‰์ƒ์ด ์„ค์ •๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๋นจ๊ฐ„์ƒ‰์œผ๋กœ ์ž๋™ ์„ค์ • if (component.style) { component.style.labelColor = "#ef4444"; } else { component.style = { labelColor: "#ef4444" }; } } }, [component.componentConfig?.action?.type, component.config?.action?.type, component.webTypeConfig?.actionType]); // ์ปดํฌ๋„ŒํŠธ ์„ค์ • // ๐Ÿ”ฅ component.componentConfig๋„ ๋ณ‘ํ•ฉํ•ด์•ผ ํ•จ (ํ™”๋ฉด ๋””์ž์ด๋„ˆ์—์„œ ์ €์žฅ๋œ ์„ค์ •) const componentConfig = { ...config, ...component.config, ...component.componentConfig, // ๐Ÿ”ฅ ํ™”๋ฉด ๋””์ž์ด๋„ˆ์—์„œ ์ €์žฅ๋œ action ๋“ฑ ํฌํ•จ } as ButtonPrimaryConfig; // ๐ŸŽจ ๋™์  ์ƒ‰์ƒ ์„ค์ • (webTypeConfig ์šฐ์„ , ๋ ˆ๊ฑฐ์‹œ style.labelColor ์ง€์›) const getButtonBackgroundColor = () => { // 1์ˆœ์œ„: webTypeConfig.backgroundColor (ํ™”๋ฉด์„ค์ • ๋ชจ๋‹ฌ์—์„œ ์ €์žฅ) if (component.webTypeConfig?.backgroundColor) { return component.webTypeConfig.backgroundColor; } // 2์ˆœ์œ„: componentConfig.backgroundColor if (componentConfig.backgroundColor) { return componentConfig.backgroundColor; } // 3์ˆœ์œ„: style.backgroundColor if (component.style?.backgroundColor) { return component.style.backgroundColor; } // 4์ˆœ์œ„: style.labelColor (๋ ˆ๊ฑฐ์‹œ) if (component.style?.labelColor) { return component.style.labelColor; } // ๊ธฐ๋ณธ๊ฐ’: ์‚ญ์ œ ๋ฒ„ํŠผ์ด๋ฉด ๋นจ๊ฐ•, ์•„๋‹ˆ๋ฉด ํŒŒ๋ž‘ if (isDeleteAction()) { return "#ef4444"; // ๋นจ๊ฐ„์ƒ‰ (Tailwind red-500) } return "#3b82f6"; // ํŒŒ๋ž€์ƒ‰ (Tailwind blue-500) }; const getButtonTextColor = () => { // 1์ˆœ์œ„: webTypeConfig.textColor (ํ™”๋ฉด์„ค์ • ๋ชจ๋‹ฌ์—์„œ ์ €์žฅ) if (component.webTypeConfig?.textColor) { return component.webTypeConfig.textColor; } // 2์ˆœ์œ„: componentConfig.textColor if (componentConfig.textColor) { return componentConfig.textColor; } // 3์ˆœ์œ„: style.color if (component.style?.color) { return component.style.color; } // ๊ธฐ๋ณธ๊ฐ’: ํฐ์ƒ‰ return "#ffffff"; }; const buttonColor = getButtonBackgroundColor(); const buttonTextColor = getButtonTextColor(); // ์•ก์…˜ ์„ค์ • ์ฒ˜๋ฆฌ - DB์—์„œ ๋ฌธ์ž์—ด๋กœ ์ €์žฅ๋œ ์•ก์…˜์„ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ const processedConfig = { ...componentConfig }; if (componentConfig.action && typeof componentConfig.action === "string") { const actionType = componentConfig.action as ButtonActionType; processedConfig.action = { ...DEFAULT_BUTTON_ACTIONS[actionType], type: actionType, // ๐Ÿ”ฅ ์ œ์–ด๊ด€๋ฆฌ ์„ค์ • ์ถ”๊ฐ€ (webTypeConfig์—์„œ ๊ฐ€์ ธ์˜ด) enableDataflowControl: component.webTypeConfig?.enableDataflowControl, dataflowConfig: component.webTypeConfig?.dataflowConfig, dataflowTiming: component.webTypeConfig?.dataflowTiming, }; } else if (componentConfig.action && typeof componentConfig.action === "object") { // ๐Ÿ”ฅ ์ด๋ฏธ ๊ฐ์ฒด์ธ ๊ฒฝ์šฐ์—๋„ ์ œ์–ด๊ด€๋ฆฌ ์„ค์ • ์ถ”๊ฐ€ processedConfig.action = { ...componentConfig.action, enableDataflowControl: component.webTypeConfig?.enableDataflowControl, dataflowConfig: component.webTypeConfig?.dataflowConfig, dataflowTiming: component.webTypeConfig?.dataflowTiming, }; } // ์Šคํƒ€์ผ ๊ณ„์‚ฐ // height: 100%๋กœ ๋ถ€๋ชจ(RealtimePreviewDynamic์˜ ๋‚ด๋ถ€ div)์˜ ๋†’์ด๋ฅผ ๋”ฐ๋ผ๊ฐ // width๋Š” ํ•ญ์ƒ 100%๋กœ ๊ณ ์ • (๋ถ€๋ชจ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ gridColumns๋กœ ํฌ๊ธฐ ์ œ์–ด) const componentStyle: React.CSSProperties = { ...component.style, ...style, width: "100%", height: "100%", }; // ๋””์ž์ธ ๋ชจ๋“œ ์Šคํƒ€์ผ (border ์†์„ฑ ๋ถ„๋ฆฌํ•˜์—ฌ ์ถฉ๋Œ ๋ฐฉ์ง€) if (isDesignMode) { componentStyle.borderWidth = "1px"; componentStyle.borderStyle = "dashed"; componentStyle.borderColor = isSelected ? "#3b82f6" : "#cbd5e1"; } // ํ™•์ธ ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ํ•„์š”ํ•œ ์•ก์…˜ ํƒ€์ž…๋“ค const confirmationRequiredActions: ButtonActionType[] = ["save", "delete"]; // ์‹ค์ œ ์•ก์…˜ ์‹คํ–‰ ํ•จ์ˆ˜ const executeAction = async (actionConfig: any, context: ButtonActionContext) => { try { // ๊ธฐ์กด ํ† ์ŠคํŠธ๊ฐ€ ์žˆ๋‹ค๋ฉด ๋จผ์ € ์ œ๊ฑฐ if (currentLoadingToastRef.current !== undefined) { toast.dismiss(currentLoadingToastRef.current); currentLoadingToastRef.current = undefined; } // ์ถ”๊ฐ€ ์•ˆ์ „์žฅ์น˜: ๋ชจ๋“  ๋กœ๋”ฉ ํ† ์ŠคํŠธ ์ œ๊ฑฐ toast.dismiss(); // UI ์ „ํ™˜ ์•ก์…˜ ๋ฐ ๋ชจ๋‹ฌ ์•ก์…˜์€ ๋กœ๋”ฉ ํ† ์ŠคํŠธ ํ‘œ์‹œํ•˜์ง€ ์•Š์Œ const silentActions = ["edit", "modal", "navigate", "excel_upload", "barcode_scan"]; if (!silentActions.includes(actionConfig.type)) { currentLoadingToastRef.current = toast.loading( actionConfig.type === "save" ? "์ €์žฅ ์ค‘..." : actionConfig.type === "delete" ? "์‚ญ์ œ ์ค‘..." : actionConfig.type === "submit" ? "์ œ์ถœ ์ค‘..." : "์ฒ˜๋ฆฌ ์ค‘...", { duration: Infinity, // ๋ช…์‹œ์ ์œผ๋กœ ๋ฌดํ•œ๋Œ€๋กœ ์„ค์ • }, ); } const success = await ButtonActionExecutor.executeAction(actionConfig, context); // ๋กœ๋”ฉ ํ† ์ŠคํŠธ ์ œ๊ฑฐ (์žˆ๋Š” ๊ฒฝ์šฐ์—๋งŒ) if (currentLoadingToastRef.current !== undefined) { toast.dismiss(currentLoadingToastRef.current); currentLoadingToastRef.current = undefined; } // ์‹คํŒจํ•œ ๊ฒฝ์šฐ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ if (!success) { // UI ์ „ํ™˜ ์•ก์…˜ ๋ฐ ๋ชจ๋‹ฌ ์•ก์…˜์€ ์—๋Ÿฌ๋„ ์กฐ์šฉํžˆ ์ฒ˜๋ฆฌ (๋ชจ๋‹ฌ ๋‚ด๋ถ€์—์„œ ์ž์ฒด ์—๋Ÿฌ ํ‘œ์‹œ) const silentErrorActions = ["edit", "modal", "navigate", "excel_upload", "barcode_scan"]; if (silentErrorActions.includes(actionConfig.type)) { return; } // ๊ธฐ๋ณธ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ๊ฒฐ์ • const defaultErrorMessage = actionConfig.type === "save" ? "์ €์žฅ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค." : actionConfig.type === "delete" ? "์‚ญ์ œ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค." : actionConfig.type === "submit" ? "์ œ์ถœ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค." : "์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."; // ์ปค์Šคํ…€ ๋ฉ”์‹œ์ง€ ์‚ฌ์šฉ ์กฐ๊ฑด: // 1. ์ปค์Šคํ…€ ๋ฉ”์‹œ์ง€๊ฐ€ ์žˆ๊ณ  // 2. (์•ก์…˜ ํƒ€์ž…์ด save์ด๊ฑฐ๋‚˜ OR ๋ฉ”์‹œ์ง€์— "์ €์žฅ"์ด ํฌํ•จ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ) const useCustomMessage = actionConfig.errorMessage && (actionConfig.type === "save" || !actionConfig.errorMessage.includes("์ €์žฅ")); const errorMessage = useCustomMessage ? actionConfig.errorMessage : defaultErrorMessage; toast.error(errorMessage); return; } // ์„ฑ๊ณตํ•œ ๊ฒฝ์šฐ์—๋งŒ ์„ฑ๊ณต ํ† ์ŠคํŠธ ํ‘œ์‹œ // edit, modal, navigate, excel_upload, barcode_scan ์•ก์…˜์€ ์กฐ์šฉํžˆ ์ฒ˜๋ฆฌ // (UI ์ „ํ™˜๋งŒ ํ•˜๊ฑฐ๋‚˜ ๋ชจ๋‹ฌ ๋‚ด๋ถ€์—์„œ ์ž์ฒด์ ์œผ๋กœ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ) const silentSuccessActions = ["edit", "modal", "navigate", "excel_upload", "barcode_scan"]; if (!silentSuccessActions.includes(actionConfig.type)) { // ๊ธฐ๋ณธ ์„ฑ๊ณต ๋ฉ”์‹œ์ง€ ๊ฒฐ์ • const defaultSuccessMessage = actionConfig.type === "save" ? "์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค." : actionConfig.type === "delete" ? "์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค." : actionConfig.type === "submit" ? "์ œ์ถœ๋˜์—ˆ์Šต๋‹ˆ๋‹ค." : "์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."; // ์ปค์Šคํ…€ ๋ฉ”์‹œ์ง€ ์‚ฌ์šฉ ์กฐ๊ฑด: // 1. ์ปค์Šคํ…€ ๋ฉ”์‹œ์ง€๊ฐ€ ์žˆ๊ณ  // 2. (์•ก์…˜ ํƒ€์ž…์ด save์ด๊ฑฐ๋‚˜ OR ๋ฉ”์‹œ์ง€์— "์ €์žฅ"์ด ํฌํ•จ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ) const useCustomMessage = actionConfig.successMessage && (actionConfig.type === "save" || !actionConfig.successMessage.includes("์ €์žฅ")); const successMessage = useCustomMessage ? actionConfig.successMessage : defaultSuccessMessage; toast.success(successMessage); } // ์ €์žฅ/์ˆ˜์ • ์„ฑ๊ณต ์‹œ ์ž๋™ ์ฒ˜๋ฆฌ if (actionConfig.type === "save" || actionConfig.type === "edit") { if (typeof window !== "undefined") { // 1. ํ…Œ์ด๋ธ” ์ƒˆ๋กœ๊ณ ์นจ ์ด๋ฒคํŠธ ๋จผ์ € ๋ฐœ์†ก (๋ชจ๋‹ฌ์ด ๋‹ซํžˆ๊ธฐ ์ „์—) window.dispatchEvent(new CustomEvent("refreshTable")); // 2. ๋ชจ๋‹ฌ ๋‹ซ๊ธฐ (์•ฝ๊ฐ„์˜ ๋”œ๋ ˆ์ด) setTimeout(() => { // EditModal ๋‚ด๋ถ€์ธ์ง€ ํ™•์ธ (isInModal prop ์‚ฌ์šฉ) const isInEditModal = (props as any).isInModal; if (isInEditModal) { window.dispatchEvent(new CustomEvent("closeEditModal")); } // ScreenModal์€ ์—ฐ์† ๋“ฑ๋ก ๋ชจ๋“œ๋ฅผ ์ง€์›ํ•˜๋ฏ€๋กœ saveSuccessInModal ์ด๋ฒคํŠธ ๋ฐœ์ƒ window.dispatchEvent(new CustomEvent("saveSuccessInModal")); }, 100); } } } catch (error) { // ๋กœ๋”ฉ ํ† ์ŠคํŠธ ์ œ๊ฑฐ if (currentLoadingToastRef.current !== undefined) { toast.dismiss(currentLoadingToastRef.current); currentLoadingToastRef.current = undefined; } console.error("โŒ ๋ฒ„ํŠผ ์•ก์…˜ ์‹คํ–‰ ์˜ค๋ฅ˜:", error); // ์˜ค๋ฅ˜ ํ† ์ŠคํŠธ๋Š” buttonActions.ts์—์„œ ์ด๋ฏธ ํ‘œ์‹œ๋˜๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ๋Š” ์ œ๊ฑฐ // (์ค‘๋ณต ํ† ์ŠคํŠธ ๋ฐฉ์ง€) } }; // ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ /** * transferData ์•ก์…˜ ์ฒ˜๋ฆฌ */ const handleTransferDataAction = async (actionConfig: any) => { const dataTransferConfig = actionConfig.dataTransfer; if (!dataTransferConfig) { toast.error("๋ฐ์ดํ„ฐ ์ „๋‹ฌ ์„ค์ •์ด ์—†์Šต๋‹ˆ๋‹ค."); return; } if (!screenContext) { toast.error("ํ™”๋ฉด ์ปจํ…์ŠคํŠธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); return; } try { // 1. ์†Œ์Šค ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ let sourceProvider = screenContext.getDataProvider(dataTransferConfig.sourceComponentId); // ๐Ÿ†• ์†Œ์Šค ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์œผ๋ฉด, ํ˜„์žฌ ํ™”๋ฉด์—์„œ ํ…Œ์ด๋ธ” ๋ฆฌ์ŠคํŠธ ์ž๋™ ํƒ์ƒ‰ // (์กฐ๊ฑด๋ถ€ ์ปจํ…Œ์ด๋„ˆ์˜ ๋‹ค๋ฅธ ์„น์…˜์œผ๋กœ ์ „ํ™˜ํ–ˆ์„ ๋•Œ ์ด์ „ ์ปดํฌ๋„ŒํŠธ ID๊ฐ€ ๋‚จ์•„์žˆ๋Š” ๊ฒฝ์šฐ ๋Œ€์‘) if (!sourceProvider) { console.log(`โš ๏ธ [ButtonPrimary] ์ง€์ •๋œ ์†Œ์Šค ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Œ: ${dataTransferConfig.sourceComponentId}`); console.log("๐Ÿ” [ButtonPrimary] ํ˜„์žฌ ํ™”๋ฉด์—์„œ DataProvider ์ž๋™ ํƒ์ƒ‰..."); const allProviders = screenContext.getAllDataProviders(); // ํ…Œ์ด๋ธ” ๋ฆฌ์ŠคํŠธ ์šฐ์„  ํƒ์ƒ‰ for (const [id, provider] of allProviders) { if (provider.componentType === "table-list") { sourceProvider = provider; console.log(`โœ… [ButtonPrimary] ํ…Œ์ด๋ธ” ๋ฆฌ์ŠคํŠธ ์ž๋™ ๋ฐœ๊ฒฌ: ${id}`); break; } } // ํ…Œ์ด๋ธ” ๋ฆฌ์ŠคํŠธ๊ฐ€ ์—†์œผ๋ฉด ์ฒซ ๋ฒˆ์งธ DataProvider ์‚ฌ์šฉ if (!sourceProvider && allProviders.size > 0) { const firstEntry = allProviders.entries().next().value; if (firstEntry) { sourceProvider = firstEntry[1]; console.log( `โœ… [ButtonPrimary] ์ฒซ ๋ฒˆ์งธ DataProvider ์‚ฌ์šฉ: ${firstEntry[0]} (${sourceProvider.componentType})`, ); } } if (!sourceProvider) { toast.error("๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); return; } } const rawSourceData = sourceProvider.getSelectedData(); // ๐Ÿ†• ๋ฐฐ์—ด์ด ์•„๋‹Œ ๊ฒฝ์šฐ ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜ const sourceData = Array.isArray(rawSourceData) ? rawSourceData : rawSourceData ? [rawSourceData] : []; console.log("๐Ÿ“ฆ ์†Œ์Šค ๋ฐ์ดํ„ฐ:", { rawSourceData, sourceData, isArray: Array.isArray(rawSourceData) }); if (!sourceData || sourceData.length === 0) { toast.warning("์„ ํƒ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค."); return; } // 1.5. ์ถ”๊ฐ€ ๋ฐ์ดํ„ฐ ์†Œ์Šค ์ฒ˜๋ฆฌ (์˜ˆ: ์กฐ๊ฑด๋ถ€ ์ปจํ…Œ์ด๋„ˆ์˜ ์นดํ…Œ๊ณ ๋ฆฌ ๊ฐ’) let additionalData: Record = {}; // ๋ฐฉ๋ฒ• 1: additionalSources ์„ค์ •์—์„œ ๊ฐ€์ ธ์˜ค๊ธฐ if (dataTransferConfig.additionalSources && Array.isArray(dataTransferConfig.additionalSources)) { for (const additionalSource of dataTransferConfig.additionalSources) { const additionalProvider = screenContext.getDataProvider(additionalSource.componentId); if (additionalProvider) { const additionalValues = additionalProvider.getSelectedData(); if (additionalValues && additionalValues.length > 0) { // ์ฒซ ๋ฒˆ์งธ ๊ฐ’ ์‚ฌ์šฉ (์กฐ๊ฑด๋ถ€ ์ปจํ…Œ์ด๋„ˆ๋Š” ํ•ญ์ƒ 1๊ฐœ) const firstValue = additionalValues[0]; // fieldName์ด ์ง€์ •๋˜์–ด ์žˆ์œผ๋ฉด ๊ทธ ํ•„๋“œ๋งŒ ์ถ”์ถœ if (additionalSource.fieldName) { additionalData[additionalSource.fieldName] = firstValue[additionalSource.fieldName] || firstValue.condition || firstValue; } else { // fieldName์ด ์—†์œผ๋ฉด ์ „์ฒด ๊ฐ์ฒด ๋ณ‘ํ•ฉ additionalData = { ...additionalData, ...firstValue }; } console.log("๐Ÿ“ฆ ์ถ”๊ฐ€ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ (additionalSources):", { sourceId: additionalSource.componentId, fieldName: additionalSource.fieldName, value: additionalData[additionalSource.fieldName || "all"], }); } } } } // ๋ฐฉ๋ฒ• 2: formData์—์„œ ์กฐ๊ฑด๋ถ€ ์ปจํ…Œ์ด๋„ˆ ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ (์ž๋™) // ConditionalSectionViewer๊ฐ€ __conditionalContainerValue, __conditionalContainerControlField๋ฅผ formData์— ํฌํ•จ์‹œํ‚ด if (formData && formData.__conditionalContainerValue) { // includeConditionalValue ์„ค์ •์ด true์ด๊ฑฐ๋‚˜ ์„ค์ •์ด ์—†์œผ๋ฉด ์ž๋™ ํฌํ•จ if (dataTransferConfig.includeConditionalValue !== false) { const conditionalValue = formData.__conditionalContainerValue; const conditionalLabel = formData.__conditionalContainerLabel; const controlField = formData.__conditionalContainerControlField; // ๐Ÿ†• ์ œ์–ด ํ•„๋“œ๋ช… ์ง์ ‘ ์‚ฌ์šฉ // ๐Ÿ†• controlField๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ๊ฒƒ์„ ํ•„๋“œ๋ช…์œผ๋กœ ์‚ฌ์šฉ (์ž๋™ ๋งคํ•‘!) if (controlField) { additionalData[controlField] = conditionalValue; console.log("๐Ÿ“ฆ ์กฐ๊ฑด๋ถ€ ์ปจํ…Œ์ด๋„ˆ ๊ฐ’ ์ž๋™ ๋งคํ•‘:", { controlField, value: conditionalValue, label: conditionalLabel, }); } else { // controlField๊ฐ€ ์—†์œผ๋ฉด ๊ธฐ์กด ๋ฐฉ์‹: formData์—์„œ ๊ฐ™์€ ๊ฐ’์„ ๊ฐ€์ง„ ํ‚ค ์ฐพ๊ธฐ for (const [key, value] of Object.entries(formData)) { if (value === conditionalValue && !key.startsWith("__")) { additionalData[key] = conditionalValue; console.log("๐Ÿ“ฆ ์กฐ๊ฑด๋ถ€ ์ปจํ…Œ์ด๋„ˆ ๊ฐ’ ์ž๋™ ํฌํ•จ:", { fieldName: key, value: conditionalValue, label: conditionalLabel, }); break; } } // ๋ชป ์ฐพ์•˜์œผ๋ฉด ๊ธฐ๋ณธ ํ•„๋“œ๋ช… ์‚ฌ์šฉ if (!Object.keys(additionalData).some((k) => !k.startsWith("__"))) { additionalData["condition_type"] = conditionalValue; console.log("๐Ÿ“ฆ ์กฐ๊ฑด๋ถ€ ์ปจํ…Œ์ด๋„ˆ ๊ฐ’ (๊ธฐ๋ณธ ํ•„๋“œ๋ช…):", { fieldName: "condition_type", value: conditionalValue, }); } } } } // 2. ๊ฒ€์ฆ const validation = dataTransferConfig.validation; if (validation) { if (validation.minSelection && sourceData.length < validation.minSelection) { toast.error(`์ตœ์†Œ ${validation.minSelection}๊ฐœ ์ด์ƒ ์„ ํƒํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.`); return; } if (validation.maxSelection && sourceData.length > validation.maxSelection) { toast.error(`์ตœ๋Œ€ ${validation.maxSelection}๊ฐœ๊นŒ์ง€ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.`); return; } } // 3. ํ™•์ธ ๋ฉ”์‹œ์ง€ if (dataTransferConfig.confirmBeforeTransfer) { const confirmMessage = dataTransferConfig.confirmMessage || `${sourceData.length}๊ฐœ ํ•ญ๋ชฉ์„ ์ „๋‹ฌํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?`; if (!window.confirm(confirmMessage)) { return; } } // 4. ๋งคํ•‘ ๊ทœ์น™ ์ ์šฉ + ์ถ”๊ฐ€ ๋ฐ์ดํ„ฐ ๋ณ‘ํ•ฉ const mappedData = sourceData.map((row) => { const mappedRow = applyMappingRules(row, dataTransferConfig.mappingRules || []); // ์ถ”๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋“  ํ–‰์— ํฌํ•จ return { ...mappedRow, ...additionalData, }; }); console.log("๐Ÿ“ฆ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ:", { sourceData, mappedData, targetType: dataTransferConfig.targetType, targetComponentId: dataTransferConfig.targetComponentId, targetScreenId: dataTransferConfig.targetScreenId, }); // 5. ํƒ€๊ฒŸ์œผ๋กœ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ if (dataTransferConfig.targetType === "component") { // ๊ฐ™์€ ํ™”๋ฉด์˜ ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌ const targetReceiver = screenContext.getDataReceiver(dataTransferConfig.targetComponentId); if (!targetReceiver) { toast.error(`ํƒ€๊ฒŸ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: ${dataTransferConfig.targetComponentId}`); return; } await targetReceiver.receiveData(mappedData, { targetComponentId: dataTransferConfig.targetComponentId, targetComponentType: targetReceiver.componentType, mode: dataTransferConfig.mode || "append", mappingRules: dataTransferConfig.mappingRules || [], }); toast.success(`${sourceData.length}๊ฐœ ํ•ญ๋ชฉ์ด ์ „๋‹ฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.`); } else if (dataTransferConfig.targetType === "splitPanel") { // ๐Ÿ†• ๋ถ„ํ•  ํŒจ๋„์˜ ๋ฐ˜๋Œ€ํŽธ ํ™”๋ฉด์œผ๋กœ ์ „๋‹ฌ if (!splitPanelContext) { toast.error("๋ถ„ํ•  ํŒจ๋„ ์ปจํ…์ŠคํŠธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด ๋ฒ„ํŠผ์ด ๋ถ„ํ•  ํŒจ๋„ ๋‚ด๋ถ€์— ์žˆ๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”."); return; } // ๐Ÿ†• useSplitPanelPosition ํ›…์œผ๋กœ ์œ„์น˜ ๊ฐ€์ ธ์˜ค๊ธฐ (์ค‘์ฒฉ๋œ ํ™”๋ฉด์—์„œ๋„ ์ž‘๋™) // screenId๋กœ ์ฐพ๋Š” ๊ฒƒ์€ ์ง์ ‘ ์ž„๋ฒ ๋“œ๋œ ํ™”๋ฉด์—์„œ๋งŒ ์ž‘๋™ํ•˜๋ฏ€๋กœ, // SplitPanelPositionProvider๋กœ ์ „๋‹ฌ๋œ ์œ„์น˜๋ฅผ ์šฐ์„  ์‚ฌ์šฉ const currentPosition = splitPanelPosition || (screenId ? splitPanelContext.getPositionByScreenId(screenId) : null); if (!currentPosition) { toast.error("๋ถ„ํ•  ํŒจ๋„ ๋‚ด ์œ„์น˜๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. screenId: " + screenId); return; } console.log("๐Ÿ“ฆ ๋ถ„ํ•  ํŒจ๋„ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ:", { currentPosition, splitPanelPositionFromHook: splitPanelPosition, screenId, leftScreenId: splitPanelContext.leftScreenId, rightScreenId: splitPanelContext.rightScreenId, }); const result = await splitPanelContext.transferToOtherSide( currentPosition, mappedData, dataTransferConfig.targetComponentId, // ํŠน์ • ์ปดํฌ๋„ŒํŠธ ์ง€์ • (์„ ํƒ์‚ฌํ•ญ) dataTransferConfig.mode || "append", ); if (result.success) { toast.success(result.message); } else { toast.error(result.message); return; } } else if (dataTransferConfig.targetType === "screen") { // ๋‹ค๋ฅธ ํ™”๋ฉด์œผ๋กœ ์ „๋‹ฌ (๊ตฌํ˜„ ์˜ˆ์ •) toast.info("๋‹ค๋ฅธ ํ™”๋ฉด์œผ๋กœ์˜ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ์€ ์ถ”ํ›„ ๊ตฌํ˜„ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค."); return; } else { toast.success(`${sourceData.length}๊ฐœ ํ•ญ๋ชฉ์ด ์ „๋‹ฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.`); } // 6. ์ „๋‹ฌ ํ›„ ์ •๋ฆฌ if (dataTransferConfig.clearAfterTransfer) { sourceProvider.clearSelection(); } } catch (error: any) { console.error("โŒ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ ์‹คํŒจ:", error); toast.error(error.message || "๋ฐ์ดํ„ฐ ์ „๋‹ฌ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."); } }; const handleClick = async (e: React.MouseEvent) => { e.stopPropagation(); // ํ”„๋ฆฌ๋ทฐ ๋ชจ๋“œ์—์„œ๋Š” ๋ฒ„ํŠผ ๋™์ž‘ ์ฐจ๋‹จ if (isPreviewMode) { return; } // ๋””์ž์ธ ๋ชจ๋“œ์—์„œ๋Š” ๊ธฐ๋ณธ onClick๋งŒ ์‹คํ–‰ if (isDesignMode) { onClick?.(); return; } // ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ๋ชจ๋“œ์—์„œ ์•ก์…˜ ์‹คํ–‰ if (isInteractive && processedConfig.action) { // transferData ์•ก์…˜ ์ฒ˜๋ฆฌ (ํ™”๋ฉด ์ปจํ…์ŠคํŠธ ํ•„์š”) if (processedConfig.action.type === "transferData") { await handleTransferDataAction(processedConfig.action); return; } // ๐Ÿ†• ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ ์šฐ์„ ์ˆœ์œ„: // 1. selectedRowsData (ํ…Œ์ด๋ธ”์—์„œ ์ง์ ‘ ์„ ํƒ) // 2. groupedData (๋ถ€๋ชจ์ฐฝ์—์„œ ๋ชจ๋‹ฌ๋กœ ์ „๋‹ฌ๋œ ๋ฐ์ดํ„ฐ) // 3. modalDataStore (๋ถ„ํ•  ํŒจ๋„ ๋“ฑ์—์„œ ์„ ํƒํ•œ ๋ฐ์ดํ„ฐ) let effectiveSelectedRowsData = selectedRowsData; // groupedData๊ฐ€ ์žˆ์œผ๋ฉด ์šฐ์„  ์‚ฌ์šฉ (๋ชจ๋‹ฌ์—์„œ ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ) if ( (!effectiveSelectedRowsData || effectiveSelectedRowsData.length === 0) && groupedData && groupedData.length > 0 ) { effectiveSelectedRowsData = groupedData; } // modalDataStore์—์„œ ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ (๋ถ„ํ•  ํŒจ๋„ ๋“ฑ์—์„œ ์„ ํƒํ•œ ๋ฐ์ดํ„ฐ) // ๋‹จ, ๋ชจ๋‹ฌ(modal) ์•ก์…˜์€ ์‹ ๊ทœ ๋“ฑ๋ก์ด๋ฏ€๋กœ modalDataStore ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค์ง€ ์•Š์Œ // (๋‹ค๋ฅธ ํ™”๋ฉด์—์„œ ์„ ํƒํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚จ์•„์žˆ์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ) const shouldFetchFromModalDataStore = processedConfig.action.type !== "modal" && (!effectiveSelectedRowsData || effectiveSelectedRowsData.length === 0) && effectiveTableName; if (shouldFetchFromModalDataStore) { try { const { useModalDataStore } = await import("@/stores/modalDataStore"); const dataRegistry = useModalDataStore.getState().dataRegistry; const modalData = dataRegistry[effectiveTableName]; if (modalData && modalData.length > 0) { // modalDataStore๋Š” {id, originalData, additionalData} ํ˜•ํƒœ๋กœ ์ €์žฅ๋จ // originalData๋ฅผ ์ถ”์ถœํ•˜์—ฌ ์‹ค์ œ ํ–‰ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ด effectiveSelectedRowsData = modalData.map((item: any) => { // originalData๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ๊ฒƒ์„ ์‚ฌ์šฉ, ์—†์œผ๋ฉด item ์ž์ฒด ์‚ฌ์šฉ (ํ•˜์œ„ ํ˜ธํ™˜์„ฑ) return item.originalData || item; }); } } catch (error) { console.warn("modalDataStore ์ ‘๊ทผ ์‹คํŒจ:", error); } } // ์‚ญ์ œ ์•ก์…˜์ธ๋ฐ ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์œผ๋ฉด ๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€ ํ‘œ์‹œํ•˜๊ณ  ์ค‘๋‹จ const hasDataToDelete = (effectiveSelectedRowsData && effectiveSelectedRowsData.length > 0) || (flowSelectedData && flowSelectedData.length > 0); if (processedConfig.action.type === "delete" && !hasDataToDelete) { toast.warning("์‚ญ์ œํ•  ํ•ญ๋ชฉ์„ ๋จผ์ € ์„ ํƒํ•ด์ฃผ์„ธ์š”."); return; } // ๐Ÿ”ง ๋ชจ๋‹ฌ ์•ก์…˜ ์‹œ ์„ ํƒ ๋ฐ์ดํ„ฐ ๊ฒฝ๊ณ  ์ œ๊ฑฐ // ์ด์ „์—๋Š” "์‹ ๊ทœ ๋“ฑ๋ก ์‹œ์—๋Š” ํ…Œ์ด๋ธ”์—์„œ ์„ ํƒ๋œ ํ•ญ๋ชฉ์„ ํ•ด์ œํ•ด์ฃผ์„ธ์š”" ๊ฒฝ๊ณ ๋ฅผ ํ‘œ์‹œํ–ˆ์œผ๋‚˜, // ๋‹ค๋ฅธ ํ™”๋ฉด์—์„œ ์„ ํƒํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚จ์•„์žˆ๋Š” ๊ฒฝ์šฐ ์˜คํƒ์ด ๋ฐœ์ƒํ•˜์—ฌ ์ œ๊ฑฐํ•จ. // ๋ชจ๋‹ฌ ํ™”๋ฉด ๋‚ด๋ถ€์—์„œ ํ•„์š” ์‹œ ์ž์ฒด์ ์œผ๋กœ ์„ ํƒ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฌด์‹œํ•˜๋„๋ก ์ฒ˜๋ฆฌํ•˜๋ฉด ๋จ. // ์ˆ˜์ •(edit) ์•ก์…˜ ๊ฒ€์ฆ if (processedConfig.action.type === "edit") { // ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์œผ๋ฉด ๊ฒฝ๊ณ  if (!effectiveSelectedRowsData || effectiveSelectedRowsData.length === 0) { toast.warning("์ˆ˜์ •ํ•  ํ•ญ๋ชฉ์„ ์„ ํƒํ•ด์ฃผ์„ธ์š”."); return; } // groupByColumns ์„ค์ •์ด ์žˆ์œผ๋ฉด ํ•ด๋‹น ์ปฌ๋Ÿผ ๊ฐ’์ด ์œ ์ผํ•œ์ง€ ํ™•์ธ const groupByColumns = processedConfig.action.groupByColumns; if (groupByColumns && groupByColumns.length > 0 && effectiveSelectedRowsData.length > 1) { // ์ฒซ ๋ฒˆ์งธ ๊ทธ๋ฃนํ•‘ ์ปฌ๋Ÿผ ๊ธฐ์ค€์œผ๋กœ ์ค‘๋ณต ์ฒดํฌ (์˜ˆ: order_no) const groupByColumn = groupByColumns[0]; const uniqueValues = new Set(effectiveSelectedRowsData.map((row: any) => row[groupByColumn]).filter(Boolean)); if (uniqueValues.size > 1) { // ์ปฌ๋Ÿผ๋ช…์„ ํ•œ๊ธ€๋กœ ๋ณ€ํ™˜ (order_no -> ์ˆ˜์ฃผ๋ฒˆํ˜ธ) const columnLabels: Record = { order_no: "์ˆ˜์ฃผ๋ฒˆํ˜ธ", shipment_no: "์ถœํ•˜๋ฒˆํ˜ธ", purchase_no: "๊ตฌ๋งค๋ฒˆํ˜ธ", }; const columnLabel = columnLabels[groupByColumn] || groupByColumn; toast.warning(`${columnLabel} ํ•˜๋‚˜๋งŒ ์„ ํƒํ•ด์ฃผ์„ธ์š”. (ํ˜„์žฌ ${uniqueValues.size}๊ฐœ ์„ ํƒ๋จ)`); return; } } } // ๐Ÿ†• ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ์˜ ์„ค์ • ์ˆ˜์ง‘ (parentDataMapping ๋“ฑ) const componentConfigs: Record = {}; if (allComponents && Array.isArray(allComponents)) { for (const comp of allComponents) { if (comp.id && comp.componentConfig) { componentConfigs[comp.id] = comp.componentConfig; } } } // ๋ถ„ํ•  ํŒจ๋„ ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ (์šฐ์ธก ํ™”๋ฉด์—์„œ ์ €์žฅ ์‹œ ์ขŒ์ธก ์„ ํƒ ๋ฐ์ดํ„ฐ ํฌํ•จ) // ์กฐ๊ฑด ์™„ํ™”: splitPanelContext๊ฐ€ ์žˆ๊ณ  selectedLeftData๊ฐ€ ์žˆ์œผ๋ฉด ๊ฐ€์ ธ์˜ด // (ํƒญ ์•ˆ์—์„œ๋„ ๋ถ„ํ•  ํŒจ๋„ ์ปจํ…์ŠคํŠธ์— ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋„๋ก) let splitPanelParentData: Record | undefined; if (splitPanelContext) { // ์šฐ์ธก ํ™”๋ฉด์ด๊ฑฐ๋‚˜, ํƒญ ์•ˆ์˜ ํ™”๋ฉด(splitPanelPosition์ด undefined)์ธ ๊ฒฝ์šฐ ๋ชจ๋‘ ์ฒ˜๋ฆฌ // ์ขŒ์ธก ํ™”๋ฉด์ด ์•„๋‹Œ ๊ฒฝ์šฐ์—๋งŒ ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ํฌํ•จ (์ขŒ์ธก์—์„œ ์ €์žฅ ์‹œ ์ž์‹ ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ€๋ชจ๋กœ ํฌํ•จํ•˜๋ฉด ์•ˆ๋จ) if (splitPanelPosition !== "left") { splitPanelParentData = splitPanelContext.getMappedParentData(); } } // ๐Ÿ†• ๋ถ„ํ•  ํŒจ๋„ ์šฐ์ธก์ด๋ฉด ์—ฌ๋Ÿฌ ์†Œ์Šค์—์„œ formData๋ฅผ ๋ณ‘ํ•ฉ // ์šฐ์„ ์ˆœ์œ„: props.formData > screenContext.formData > splitPanelParentData const screenContextFormData = screenContext?.formData || {}; const propsFormData = formData || {}; // ๋ณ‘ํ•ฉ: splitPanelParentData๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ, props.formData, screenContext.formData ์ˆœ์œผ๋กœ ์˜ค๋ฒ„๋ผ์ด๋“œ // (์ผ๋ฐ˜ ํผ ํ•„๋“œ๋Š” props.formData, RepeaterFieldGroup์€ screenContext.formData์— ์žˆ์Œ) let effectiveFormData = { ...propsFormData, ...screenContextFormData }; // ๋ถ„ํ•  ํŒจ๋„ ์šฐ์ธก์ด๊ณ  formData๊ฐ€ ๋น„์–ด์žˆ์œผ๋ฉด splitPanelParentData ์‚ฌ์šฉ if (splitPanelPosition === "right" && Object.keys(effectiveFormData).length === 0 && splitPanelParentData) { effectiveFormData = { ...splitPanelParentData }; } const context: ButtonActionContext = { formData: effectiveFormData, originalData: originalData, // ๐Ÿ”ง ๋นˆ ๊ฐ์ฒด ๋Œ€์‹  undefined ์œ ์ง€ (UPDATE ํŒ๋‹จ์— ์‚ฌ์šฉ) screenId: effectiveScreenId, // ๐Ÿ†• ScreenContext์—์„œ ๊ฐ€์ ธ์˜จ ๊ฐ’ ์‚ฌ์šฉ tableName: effectiveTableName, // ๐Ÿ†• ScreenContext์—์„œ ๊ฐ€์ ธ์˜จ ๊ฐ’ ์‚ฌ์šฉ userId, // ๐Ÿ†• ์‚ฌ์šฉ์ž ID userName, // ๐Ÿ†• ์‚ฌ์šฉ์ž ์ด๋ฆ„ companyCode, // ๐Ÿ†• ํšŒ์‚ฌ ์ฝ”๋“œ onFormDataChange, onRefresh, onClose, onFlowRefresh, // ํ”Œ๋กœ์šฐ ์ƒˆ๋กœ๊ณ ์นจ ์ฝœ๋ฐฑ ์ถ”๊ฐ€ onSave: finalOnSave, // ๐Ÿ†• EditModal์˜ handleSave ์ฝœ๋ฐฑ (props์—์„œ๋„ ์ถ”์ถœ) // ํ…Œ์ด๋ธ” ์„ ํƒ๋œ ํ–‰ ์ •๋ณด ์ถ”๊ฐ€ (modalDataStore์—์„œ ๊ฐ€์ ธ์˜จ ๋ฐ์ดํ„ฐ ์šฐ์„ ) selectedRows, selectedRowsData: effectiveSelectedRowsData, // ํ…Œ์ด๋ธ” ์ •๋ ฌ ์ •๋ณด ์ถ”๊ฐ€ sortBy, // ๐Ÿ†• ์ •๋ ฌ ์ปฌ๋Ÿผ sortOrder, // ๐Ÿ†• ์ •๋ ฌ ๋ฐฉํ–ฅ columnOrder, // ๐Ÿ†• ์ปฌ๋Ÿผ ์ˆœ์„œ tableDisplayData, // ๐Ÿ†• ํ™”๋ฉด์— ํ‘œ์‹œ๋œ ๋ฐ์ดํ„ฐ // ๐Ÿ†• ๊ฐ™์€ ํ™”๋ฉด์˜ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ (TableList ์ž๋™ ๊ฐ์ง€์šฉ) allComponents, // ํ”Œ๋กœ์šฐ ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ ์ •๋ณด ์ถ”๊ฐ€ flowSelectedData, flowSelectedStepId, // ๐Ÿ†• ์ปดํฌ๋„ŒํŠธ๋ณ„ ์„ค์ • (parentDataMapping ๋“ฑ) componentConfigs, // ๐Ÿ†• ๋ถ„ํ•  ํŒจ๋„ ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ (์ขŒ์ธก ํ™”๋ฉด์—์„œ ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ) splitPanelParentData, // ๐Ÿ†• ๋ถ„ํ•  ํŒจ๋„ ์ปจํ…์ŠคํŠธ (quickInsert ๋“ฑ์—์„œ ์ขŒ์ธก ํŒจ๋„ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ์šฉ) splitPanelContext: splitPanelContext ? { selectedLeftData: splitPanelContext.selectedLeftData, refreshRightPanel: splitPanelContext.refreshRightPanel, } : undefined, } as ButtonActionContext; // ํ™•์ธ์ด ํ•„์š”ํ•œ ์•ก์…˜์ธ์ง€ ํ™•์ธ (save/delete๋งŒ ํ™•์ธ ๋‹ค์ด์–ผ๋กœ๊ทธ ํ‘œ์‹œ) if (confirmationRequiredActions.includes(processedConfig.action.type)) { // ํ™•์ธ ๋‹ค์ด์–ผ๋กœ๊ทธ ํ‘œ์‹œ setPendingAction({ type: processedConfig.action.type, config: processedConfig.action, context, }); setShowConfirmDialog(true); } else { // ํ™•์ธ์ด ํ•„์š”ํ•˜์ง€ ์•Š์€ ์•ก์…˜์€ ๋ฐ”๋กœ ์‹คํ–‰ await executeAction(processedConfig.action, context); } } else { // ์•ก์…˜์ด ์„ค์ •๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๊ธฐ๋ณธ onClick ์‹คํ–‰ onClick?.(); } }; // ํ™•์ธ ๋‹ค์ด์–ผ๋กœ๊ทธ์—์„œ ํ™•์ธ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ const handleConfirmAction = async () => { if (pendingAction) { await executeAction(pendingAction.config, pendingAction.context); } setShowConfirmDialog(false); setPendingAction(null); }; // ํ™•์ธ ๋‹ค์ด์–ผ๋กœ๊ทธ์—์„œ ์ทจ์†Œ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ const handleCancelAction = () => { setShowConfirmDialog(false); setPendingAction(null); }; // DOM์— ์ „๋‹ฌํ•˜๋ฉด ์•ˆ ๋˜๋Š” React-specific props ํ•„ํ„ฐ๋ง const { selectedScreen, onZoneComponentDrop, onZoneClick, componentConfig: _componentConfig, component: _component, isSelected: _isSelected, onClick: _onClick, onDragStart: _onDragStart, onDragEnd: _onDragEnd, size: _size, position: _position, style: _style, screenId: _screenId, tableName: _tableName, onRefresh: _onRefresh, onClose: _onClose, selectedRows: _selectedRows, selectedRowsData: _selectedRowsData, onSelectedRowsChange: _onSelectedRowsChange, flowSelectedData: _flowSelectedData, // ํ”Œ๋กœ์šฐ ์„ ํƒ ๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง flowSelectedStepId: _flowSelectedStepId, // ํ”Œ๋กœ์šฐ ์„ ํƒ ์Šคํ… ID ํ•„ํ„ฐ๋ง onFlowRefresh: _onFlowRefresh, // ํ”Œ๋กœ์šฐ ์ƒˆ๋กœ๊ณ ์นจ ์ฝœ๋ฐฑ ํ•„ํ„ฐ๋ง originalData: _originalData, // ๋ถ€๋ถ„ ์—…๋ฐ์ดํŠธ์šฉ ์›๋ณธ ๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง _originalData: __originalData, // DOM ํ•„ํ„ฐ๋ง _initialData: __initialData, // DOM ํ•„ํ„ฐ๋ง _groupedData: __groupedData, // DOM ํ•„ํ„ฐ๋ง refreshKey: _refreshKey, // ํ•„ํ„ฐ๋ง ์ถ”๊ฐ€ isInModal: _isInModal, // ํ•„ํ„ฐ๋ง ์ถ”๊ฐ€ mode: _mode, // ํ•„ํ„ฐ๋ง ์ถ”๊ฐ€ ...domProps } = props; // ๋‹ค์ด์–ผ๋กœ๊ทธ ๋ฉ”์‹œ์ง€ ์ƒ์„ฑ const getConfirmMessage = () => { if (!pendingAction) return ""; const customMessage = pendingAction.config.confirmMessage; if (customMessage) return customMessage; switch (pendingAction.type) { case "save": return "๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์ €์žฅํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?"; case "delete": return "์ •๋ง๋กœ ์‚ญ์ œํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ์ด ์ž‘์—…์€ ๋˜๋Œ๋ฆด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."; case "submit": return "์ œ์ถœํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?"; default: return "์ด ์ž‘์—…์„ ์‹คํ–‰ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?"; } }; const getConfirmTitle = () => { if (!pendingAction) return ""; switch (pendingAction.type) { case "save": return "์ €์žฅ ํ™•์ธ"; case "delete": return "์‚ญ์ œ ํ™•์ธ"; case "submit": return "์ œ์ถœ ํ™•์ธ"; default: return "์ž‘์—… ํ™•์ธ"; } }; // DOM ์•ˆ์ „ํ•œ props๋งŒ ํ•„ํ„ฐ๋ง const safeDomProps = filterDOMProps(domProps); // ๐Ÿ†• ํ”Œ๋กœ์šฐ ๋‹จ๊ณ„๋ณ„ ํ‘œ์‹œ ์ œ์–ด if (!shouldShowButton) { // ๋ ˆ์ด์•„์›ƒ ๋™์ž‘์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ์ฒ˜๋ฆฌ if (flowConfig?.layoutBehavior === "preserve-position") { // ์œ„์น˜ ์œ ์ง€ (๋นˆ ๊ณต๊ฐ„, display: none) return
; } else { // ์™„์ „ํžˆ ๋ Œ๋”๋งํ•˜์ง€ ์•Š์Œ (auto-compact, ๋นˆ ๊ณต๊ฐ„ ์ œ๊ฑฐ) return null; } } // ๐Ÿ†• ์ตœ์ข… ๋น„ํ™œ์„ฑํ™” ์ƒํƒœ (์„ค์ • + ์กฐ๊ฑด๋ถ€ ๋น„ํ™œ์„ฑํ™” + ํ–‰ ์„ ํƒ ํ•„์ˆ˜) const finalDisabled = componentConfig.disabled || isOperationButtonDisabled || isRowSelectionDisabled || statusLoading; // ๊ณตํ†ต ๋ฒ„ํŠผ ์Šคํƒ€์ผ // ๐Ÿ”ง component.style์—์„œ background/backgroundColor ์ถฉ๋Œ ๋ฐฉ์ง€ const userStyle = component.style ? Object.fromEntries( Object.entries(component.style).filter( ([key]) => !["width", "height", "background", "backgroundColor"].includes(key), ), ) : {}; const buttonElementStyle: React.CSSProperties = { width: "100%", height: "100%", minHeight: "40px", border: "none", borderRadius: "0.5rem", backgroundColor: finalDisabled ? "#e5e7eb" : buttonColor, color: finalDisabled ? "#9ca3af" : buttonTextColor, // ๐Ÿ”ง webTypeConfig.textColor ์ง€์› // ๐Ÿ”ง ํฌ๊ธฐ ์„ค์ • ์ ์šฉ (sm/md/lg) fontSize: componentConfig.size === "sm" ? "0.75rem" : componentConfig.size === "lg" ? "1rem" : "0.875rem", fontWeight: "600", cursor: finalDisabled ? "not-allowed" : "pointer", outline: "none", boxSizing: "border-box", display: "flex", alignItems: "center", justifyContent: "center", // ๐Ÿ”ง ํฌ๊ธฐ์— ๋”ฐ๋ฅธ ํŒจ๋”ฉ ์กฐ์ • padding: componentConfig.size === "sm" ? "0 0.75rem" : componentConfig.size === "lg" ? "0 1.25rem" : "0 1rem", margin: "0", lineHeight: "1.25", boxShadow: finalDisabled ? "none" : "0 1px 2px 0 rgba(0, 0, 0, 0.05)", // ๋””์ž์ธ ๋ชจ๋“œ์™€ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ๋ชจ๋“œ ๋ชจ๋‘์—์„œ ์‚ฌ์šฉ์ž ์Šคํƒ€์ผ ์ ์šฉ (width/height/background ์ œ์™ธ) ...userStyle, }; const buttonContent = processedConfig.text !== undefined ? processedConfig.text : component.label || "๋ฒ„ํŠผ"; return ( <>
{isDesignMode ? ( // ๋””์ž์ธ ๋ชจ๋“œ: div๋กœ ๋ Œ๋”๋งํ•˜์—ฌ ์„ ํƒ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•จ
{buttonContent}
) : ( // ์ผ๋ฐ˜ ๋ชจ๋“œ: button์œผ๋กœ ๋ Œ๋”๋ง )}
{/* ํ™•์ธ ๋‹ค์ด์–ผ๋กœ๊ทธ - EditModal๋ณด๋‹ค ์œ„์— ํ‘œ์‹œํ•˜๋„๋ก z-index ์ตœ์ƒ์œ„๋กœ ์„ค์ • */} {getConfirmTitle()} {getConfirmMessage()} ์ทจ์†Œ {pendingAction?.type === "save" ? "์ €์žฅ" : pendingAction?.type === "delete" ? "์‚ญ์ œ" : pendingAction?.type === "submit" ? "์ œ์ถœ" : "ํ™•์ธ"} ); }; /** * ButtonPrimary ๋ž˜ํผ ์ปดํฌ๋„ŒํŠธ * ์ถ”๊ฐ€์ ์ธ ๋กœ์ง์ด๋‚˜ ์ƒํƒœ ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์‚ฌ์šฉ */ export const ButtonPrimaryWrapper: React.FC = (props) => { return ; };