Compare commits
4 Commits
67e838dc03
...
23be8a0eee
| Author | SHA1 | Date |
|---|---|---|
|
|
23be8a0eee | |
|
|
2f51b9632d | |
|
|
43654f7516 | |
|
|
c228ddb498 |
|
|
@ -34,6 +34,9 @@ export default function ScreenViewPage() {
|
||||||
// 테이블 새로고침을 위한 키 (값이 변경되면 테이블이 리렌더링됨)
|
// 테이블 새로고침을 위한 키 (값이 변경되면 테이블이 리렌더링됨)
|
||||||
const [tableRefreshKey, setTableRefreshKey] = useState(0);
|
const [tableRefreshKey, setTableRefreshKey] = useState(0);
|
||||||
|
|
||||||
|
// 플로우 새로고침을 위한 키 (값이 변경되면 플로우 데이터가 리렌더링됨)
|
||||||
|
const [flowRefreshKey, setFlowRefreshKey] = useState(0);
|
||||||
|
|
||||||
// 편집 모달 상태
|
// 편집 모달 상태
|
||||||
const [editModalOpen, setEditModalOpen] = useState(false);
|
const [editModalOpen, setEditModalOpen] = useState(false);
|
||||||
const [editModalConfig, setEditModalConfig] = useState<{
|
const [editModalConfig, setEditModalConfig] = useState<{
|
||||||
|
|
@ -250,6 +253,13 @@ export default function ScreenViewPage() {
|
||||||
setTableRefreshKey((prev) => prev + 1);
|
setTableRefreshKey((prev) => prev + 1);
|
||||||
setSelectedRowsData([]); // 선택 해제
|
setSelectedRowsData([]); // 선택 해제
|
||||||
}}
|
}}
|
||||||
|
flowRefreshKey={flowRefreshKey}
|
||||||
|
onFlowRefresh={() => {
|
||||||
|
console.log("🔄 플로우 새로고침 요청됨");
|
||||||
|
setFlowRefreshKey((prev) => prev + 1);
|
||||||
|
setFlowSelectedData([]); // 선택 해제
|
||||||
|
setFlowSelectedStepId(null);
|
||||||
|
}}
|
||||||
formData={formData}
|
formData={formData}
|
||||||
onFormDataChange={(fieldName, value) => {
|
onFormDataChange={(fieldName, value) => {
|
||||||
console.log("📝 폼 데이터 변경:", fieldName, "=", value);
|
console.log("📝 폼 데이터 변경:", fieldName, "=", value);
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@ interface RealtimePreviewProps {
|
||||||
onFlowSelectedDataChange?: (selectedData: any[], stepId: number | null) => void;
|
onFlowSelectedDataChange?: (selectedData: any[], stepId: number | null) => void;
|
||||||
refreshKey?: number;
|
refreshKey?: number;
|
||||||
onRefresh?: () => void;
|
onRefresh?: () => void;
|
||||||
|
flowRefreshKey?: number;
|
||||||
|
onFlowRefresh?: () => void;
|
||||||
|
|
||||||
// 폼 데이터 관련 props
|
// 폼 데이터 관련 props
|
||||||
formData?: Record<string, any>;
|
formData?: Record<string, any>;
|
||||||
|
|
@ -101,6 +103,8 @@ export const RealtimePreviewDynamic: React.FC<RealtimePreviewProps> = ({
|
||||||
onFlowSelectedDataChange,
|
onFlowSelectedDataChange,
|
||||||
refreshKey,
|
refreshKey,
|
||||||
onRefresh,
|
onRefresh,
|
||||||
|
flowRefreshKey,
|
||||||
|
onFlowRefresh,
|
||||||
formData,
|
formData,
|
||||||
onFormDataChange,
|
onFormDataChange,
|
||||||
}) => {
|
}) => {
|
||||||
|
|
@ -299,6 +303,8 @@ export const RealtimePreviewDynamic: React.FC<RealtimePreviewProps> = ({
|
||||||
onFlowSelectedDataChange={onFlowSelectedDataChange}
|
onFlowSelectedDataChange={onFlowSelectedDataChange}
|
||||||
refreshKey={refreshKey}
|
refreshKey={refreshKey}
|
||||||
onRefresh={onRefresh}
|
onRefresh={onRefresh}
|
||||||
|
flowRefreshKey={flowRefreshKey}
|
||||||
|
onFlowRefresh={onFlowRefresh}
|
||||||
formData={formData}
|
formData={formData}
|
||||||
onFormDataChange={onFormDataChange}
|
onFormDataChange={onFormDataChange}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,11 @@ interface FlowWidgetProps {
|
||||||
component: FlowComponent;
|
component: FlowComponent;
|
||||||
onStepClick?: (stepId: number, stepName: string) => void;
|
onStepClick?: (stepId: number, stepName: string) => void;
|
||||||
onSelectedDataChange?: (selectedData: any[], stepId: number | null) => void;
|
onSelectedDataChange?: (selectedData: any[], stepId: number | null) => void;
|
||||||
|
flowRefreshKey?: number; // 새로고침 키
|
||||||
|
onFlowRefresh?: () => void; // 새로고침 완료 콜백
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FlowWidget({ component, onStepClick, onSelectedDataChange }: FlowWidgetProps) {
|
export function FlowWidget({ component, onStepClick, onSelectedDataChange, flowRefreshKey, onFlowRefresh }: FlowWidgetProps) {
|
||||||
const [flowData, setFlowData] = useState<FlowDefinition | null>(null);
|
const [flowData, setFlowData] = useState<FlowDefinition | null>(null);
|
||||||
const [steps, setSteps] = useState<FlowStep[]>([]);
|
const [steps, setSteps] = useState<FlowStep[]>([]);
|
||||||
const [stepCounts, setStepCounts] = useState<Record<number, number>>({});
|
const [stepCounts, setStepCounts] = useState<Record<number, number>>({});
|
||||||
|
|
@ -66,23 +68,66 @@ export function FlowWidget({ component, onStepClick, onSelectedDataChange }: Flo
|
||||||
const showStepCount = config.showStepCount !== false && component.showStepCount !== false; // 기본값 true
|
const showStepCount = config.showStepCount !== false && component.showStepCount !== false; // 기본값 true
|
||||||
const allowDataMove = config.allowDataMove || component.allowDataMove || false;
|
const allowDataMove = config.allowDataMove || component.allowDataMove || false;
|
||||||
|
|
||||||
console.log("🔍 FlowWidget 렌더링:", {
|
|
||||||
component,
|
// 선택된 스텝의 데이터를 다시 로드하는 함수
|
||||||
componentConfig: config,
|
const refreshStepData = async () => {
|
||||||
flowId,
|
if (!flowId) return;
|
||||||
flowName,
|
|
||||||
displayMode,
|
try {
|
||||||
showStepCount,
|
// 스텝 카운트는 항상 업데이트 (선택된 스텝 유무와 관계없이)
|
||||||
allowDataMove,
|
const countsResponse = await getAllStepCounts(flowId);
|
||||||
|
console.log("📊 스텝 카운트 API 응답:", countsResponse);
|
||||||
|
|
||||||
|
if (countsResponse.success && countsResponse.data) {
|
||||||
|
// Record 형태로 변환
|
||||||
|
const countsMap: Record<number, number> = {};
|
||||||
|
if (Array.isArray(countsResponse.data)) {
|
||||||
|
countsResponse.data.forEach((item: any) => {
|
||||||
|
countsMap[item.stepId] = item.count;
|
||||||
});
|
});
|
||||||
|
} else if (typeof countsResponse.data === 'object') {
|
||||||
|
Object.assign(countsMap, countsResponse.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("✅ 스텝 카운트 업데이트:", countsMap);
|
||||||
|
setStepCounts(countsMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 선택된 스텝이 있으면 해당 스텝의 데이터도 새로고침
|
||||||
|
if (selectedStepId) {
|
||||||
|
setStepDataLoading(true);
|
||||||
|
|
||||||
|
const response = await getStepDataList(flowId, selectedStepId, 1, 100);
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
throw new Error(response.message || "데이터를 불러올 수 없습니다");
|
||||||
|
}
|
||||||
|
|
||||||
|
const rows = response.data?.records || [];
|
||||||
|
setStepData(rows);
|
||||||
|
|
||||||
|
// 컬럼 추출
|
||||||
|
if (rows.length > 0) {
|
||||||
|
setStepDataColumns(Object.keys(rows[0]));
|
||||||
|
} else {
|
||||||
|
setStepDataColumns([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 선택 초기화
|
||||||
|
setSelectedRows(new Set());
|
||||||
|
onSelectedDataChange?.([], selectedStepId);
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error("❌ 플로우 새로고침 실패:", err);
|
||||||
|
toast.error(err.message || "데이터를 새로고치는데 실패했습니다");
|
||||||
|
} finally {
|
||||||
|
if (selectedStepId) {
|
||||||
|
setStepDataLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("🔍 FlowWidget useEffect 실행:", {
|
|
||||||
flowId,
|
|
||||||
hasFlowId: !!flowId,
|
|
||||||
config,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!flowId) {
|
if (!flowId) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
return;
|
return;
|
||||||
|
|
@ -144,6 +189,14 @@ export function FlowWidget({ component, onStepClick, onSelectedDataChange }: Flo
|
||||||
loadFlowData();
|
loadFlowData();
|
||||||
}, [flowId, showStepCount]);
|
}, [flowId, showStepCount]);
|
||||||
|
|
||||||
|
// flowRefreshKey가 변경될 때마다 스텝 데이터 새로고침
|
||||||
|
useEffect(() => {
|
||||||
|
if (flowRefreshKey !== undefined && flowRefreshKey > 0 && flowId) {
|
||||||
|
console.log("🔄 플로우 새로고침 실행, flowRefreshKey:", flowRefreshKey);
|
||||||
|
refreshStepData();
|
||||||
|
}
|
||||||
|
}, [flowRefreshKey]);
|
||||||
|
|
||||||
// 스텝 클릭 핸들러
|
// 스텝 클릭 핸들러
|
||||||
const handleStepClick = async (stepId: number, stepName: string) => {
|
const handleStepClick = async (stepId: number, stepName: string) => {
|
||||||
if (onStepClick) {
|
if (onStepClick) {
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,9 @@ export interface DynamicComponentRendererProps {
|
||||||
onFlowSelectedDataChange?: (selectedData: any[], stepId: number | null) => void;
|
onFlowSelectedDataChange?: (selectedData: any[], stepId: number | null) => void;
|
||||||
// 테이블 새로고침 키
|
// 테이블 새로고침 키
|
||||||
refreshKey?: number;
|
refreshKey?: number;
|
||||||
|
// 플로우 새로고침 키
|
||||||
|
flowRefreshKey?: number;
|
||||||
|
onFlowRefresh?: () => void;
|
||||||
// 편집 모드
|
// 편집 모드
|
||||||
mode?: "view" | "edit";
|
mode?: "view" | "edit";
|
||||||
// 모달 내에서 렌더링 여부
|
// 모달 내에서 렌더링 여부
|
||||||
|
|
@ -186,6 +189,8 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
||||||
flowSelectedStepId,
|
flowSelectedStepId,
|
||||||
onFlowSelectedDataChange,
|
onFlowSelectedDataChange,
|
||||||
refreshKey,
|
refreshKey,
|
||||||
|
flowRefreshKey, // Added this
|
||||||
|
onFlowRefresh, // Added this
|
||||||
onConfigChange,
|
onConfigChange,
|
||||||
isPreview,
|
isPreview,
|
||||||
autoGeneration,
|
autoGeneration,
|
||||||
|
|
@ -307,6 +312,9 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
||||||
// 설정 변경 핸들러 전달
|
// 설정 변경 핸들러 전달
|
||||||
onConfigChange,
|
onConfigChange,
|
||||||
refreshKey,
|
refreshKey,
|
||||||
|
// 플로우 새로고침 키
|
||||||
|
flowRefreshKey,
|
||||||
|
onFlowRefresh,
|
||||||
// 반응형 모드 플래그 전달
|
// 반응형 모드 플래그 전달
|
||||||
isPreview,
|
isPreview,
|
||||||
// 디자인 모드 플래그 전달 - isPreview와 명확히 구분
|
// 디자인 모드 플래그 전달 - isPreview와 명확히 구분
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ export interface ButtonPrimaryComponentProps extends ComponentRendererProps {
|
||||||
tableName?: string;
|
tableName?: string;
|
||||||
onRefresh?: () => void;
|
onRefresh?: () => void;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
|
onFlowRefresh?: () => void;
|
||||||
|
|
||||||
// 폼 데이터 관련
|
// 폼 데이터 관련
|
||||||
originalData?: Record<string, any>; // 부분 업데이트용 원본 데이터
|
originalData?: Record<string, any>; // 부분 업데이트용 원본 데이터
|
||||||
|
|
@ -64,6 +65,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||||
tableName,
|
tableName,
|
||||||
onRefresh,
|
onRefresh,
|
||||||
onClose,
|
onClose,
|
||||||
|
onFlowRefresh,
|
||||||
selectedRows,
|
selectedRows,
|
||||||
selectedRowsData,
|
selectedRowsData,
|
||||||
flowSelectedData,
|
flowSelectedData,
|
||||||
|
|
@ -418,8 +420,14 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||||
});
|
});
|
||||||
|
|
||||||
// 삭제 액션인데 선택된 데이터가 없으면 경고 메시지 표시하고 중단
|
// 삭제 액션인데 선택된 데이터가 없으면 경고 메시지 표시하고 중단
|
||||||
if (processedConfig.action.type === "delete" && (!selectedRowsData || selectedRowsData.length === 0)) {
|
const hasDataToDelete =
|
||||||
console.log("⚠️ 삭제할 데이터가 선택되지 않았습니다.");
|
(selectedRowsData && selectedRowsData.length > 0) || (flowSelectedData && flowSelectedData.length > 0);
|
||||||
|
|
||||||
|
if (processedConfig.action.type === "delete" && !hasDataToDelete) {
|
||||||
|
console.log("⚠️ 삭제할 데이터가 선택되지 않았습니다.", {
|
||||||
|
hasSelectedRowsData: !!(selectedRowsData && selectedRowsData.length > 0),
|
||||||
|
hasFlowSelectedData: !!(flowSelectedData && flowSelectedData.length > 0),
|
||||||
|
});
|
||||||
toast.warning("삭제할 항목을 먼저 선택해주세요.");
|
toast.warning("삭제할 항목을 먼저 선택해주세요.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -432,6 +440,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||||
onFormDataChange,
|
onFormDataChange,
|
||||||
onRefresh,
|
onRefresh,
|
||||||
onClose,
|
onClose,
|
||||||
|
onFlowRefresh, // 플로우 새로고침 콜백 추가
|
||||||
// 테이블 선택된 행 정보 추가
|
// 테이블 선택된 행 정보 추가
|
||||||
selectedRows,
|
selectedRows,
|
||||||
selectedRowsData,
|
selectedRowsData,
|
||||||
|
|
@ -517,6 +526,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||||
onSelectedRowsChange: _onSelectedRowsChange,
|
onSelectedRowsChange: _onSelectedRowsChange,
|
||||||
flowSelectedData: _flowSelectedData, // 플로우 선택 데이터 필터링
|
flowSelectedData: _flowSelectedData, // 플로우 선택 데이터 필터링
|
||||||
flowSelectedStepId: _flowSelectedStepId, // 플로우 선택 스텝 ID 필터링
|
flowSelectedStepId: _flowSelectedStepId, // 플로우 선택 스텝 ID 필터링
|
||||||
|
onFlowRefresh: _onFlowRefresh, // 플로우 새로고침 콜백 필터링
|
||||||
originalData: _originalData, // 부분 업데이트용 원본 데이터 필터링
|
originalData: _originalData, // 부분 업데이트용 원본 데이터 필터링
|
||||||
refreshKey: _refreshKey, // 필터링 추가
|
refreshKey: _refreshKey, // 필터링 추가
|
||||||
isInModal: _isInModal, // 필터링 추가
|
isInModal: _isInModal, // 필터링 추가
|
||||||
|
|
|
||||||
|
|
@ -20,23 +20,12 @@ export class FlowWidgetRenderer extends AutoRegisteringComponentRenderer {
|
||||||
})();
|
})();
|
||||||
|
|
||||||
render(): React.ReactElement {
|
render(): React.ReactElement {
|
||||||
console.log("🎨🎨🎨 FlowWidgetRenderer - render 호출 시작 🎨🎨🎨");
|
|
||||||
console.log("🎨 FlowWidgetRenderer - render 호출:", {
|
|
||||||
componentId: this.props.component.id,
|
|
||||||
hasOnFlowSelectedDataChange: !!this.props.onFlowSelectedDataChange,
|
|
||||||
onFlowSelectedDataChangeType: typeof this.props.onFlowSelectedDataChange,
|
|
||||||
allPropsKeys: Object.keys(this.props),
|
|
||||||
allPropsValues: this.props,
|
|
||||||
});
|
|
||||||
console.log("🎨🎨🎨 FlowWidget에 전달할 prop:", {
|
|
||||||
hasComponent: !!this.props.component,
|
|
||||||
hasOnSelectedDataChange: !!this.props.onFlowSelectedDataChange,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FlowWidget
|
<FlowWidget
|
||||||
component={this.props.component as any}
|
component={this.props.component as any}
|
||||||
onSelectedDataChange={this.props.onFlowSelectedDataChange}
|
onSelectedDataChange={this.props.onFlowSelectedDataChange}
|
||||||
|
flowRefreshKey={this.props.flowRefreshKey}
|
||||||
|
onFlowRefresh={this.props.onFlowRefresh}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ export interface ButtonActionContext {
|
||||||
onFormDataChange?: (fieldName: string, value: any) => void;
|
onFormDataChange?: (fieldName: string, value: any) => void;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
onRefresh?: () => void;
|
onRefresh?: () => void;
|
||||||
|
onFlowRefresh?: () => void; // 플로우 새로고침 콜백
|
||||||
|
|
||||||
// 테이블 선택된 행 정보 (다중 선택 액션용)
|
// 테이블 선택된 행 정보 (다중 선택 액션용)
|
||||||
selectedRows?: any[];
|
selectedRows?: any[];
|
||||||
|
|
@ -216,7 +217,9 @@ export class ButtonActionExecutor {
|
||||||
throw new Error("저장에 필요한 정보가 부족합니다. (테이블명 또는 화면ID 누락)");
|
throw new Error("저장에 필요한 정보가 부족합니다. (테이블명 또는 화면ID 누락)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 테이블과 플로우 모두 새로고침
|
||||||
context.onRefresh?.();
|
context.onRefresh?.();
|
||||||
|
context.onFlowRefresh?.();
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("저장 오류:", error);
|
console.error("저장 오류:", error);
|
||||||
|
|
@ -402,7 +405,16 @@ export class ButtonActionExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`✅ 다중 삭제 성공: ${dataToDelete.length}개 항목`);
|
console.log(`✅ 다중 삭제 성공: ${dataToDelete.length}개 항목`);
|
||||||
|
|
||||||
|
// 데이터 소스에 따라 적절한 새로고침 호출
|
||||||
|
if (flowSelectedData && flowSelectedData.length > 0) {
|
||||||
|
console.log("🔄 플로우 데이터 삭제 완료, 플로우 새로고침 호출");
|
||||||
|
context.onFlowRefresh?.(); // 플로우 새로고침
|
||||||
|
} else {
|
||||||
|
console.log("🔄 테이블 데이터 삭제 완료, 테이블 새로고침 호출");
|
||||||
context.onRefresh?.(); // 테이블 새로고침
|
context.onRefresh?.(); // 테이블 새로고침
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue