[agent-pipeline] pipe-20260309055714-23ry round-2
This commit is contained in:
parent
197ddf47cf
commit
790592ec76
|
|
@ -274,7 +274,6 @@ export function DashboardViewer({
|
||||||
|
|
||||||
console.log("📸 html-to-image 로딩 중...");
|
console.log("📸 html-to-image 로딩 중...");
|
||||||
// html-to-image 동적 import
|
// html-to-image 동적 import
|
||||||
// @ts-expect-error - html-to-image 타입 선언 누락
|
|
||||||
const { toPng } = await import("html-to-image");
|
const { toPng } = await import("html-to-image");
|
||||||
|
|
||||||
console.log("📸 캔버스 캡처 중...");
|
console.log("📸 캔버스 캡처 중...");
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,6 @@ import { ConnectionStep } from "./RightPanel/ConnectionStep";
|
||||||
import { TableStep } from "./RightPanel/TableStep";
|
import { TableStep } from "./RightPanel/TableStep";
|
||||||
import { FieldMappingStep } from "./RightPanel/FieldMappingStep";
|
import { FieldMappingStep } from "./RightPanel/FieldMappingStep";
|
||||||
import { DataConnectionState } from "./types/redesigned";
|
import { DataConnectionState } from "./types/redesigned";
|
||||||
import { ResponsiveContainer, ResponsiveGrid } from "@/components/layout/ResponsiveContainer";
|
|
||||||
import { useResponsive } from "@/lib/hooks/useResponsive";
|
|
||||||
|
|
||||||
const initialState: DataConnectionState = {
|
const initialState: DataConnectionState = {
|
||||||
connectionType: "data_save",
|
connectionType: "data_save",
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,45 @@ import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Database, Filter, Zap, CheckCircle, XCircle, Edit } from "lucide-react";
|
import { Database, Filter, Zap, CheckCircle, XCircle, Edit } from "lucide-react";
|
||||||
import { DataConnectionState } from "../types/redesigned";
|
interface TableRef {
|
||||||
|
tableName?: string;
|
||||||
|
tableLabel?: string;
|
||||||
|
displayName?: string;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ColumnRef {
|
||||||
|
columnName?: string;
|
||||||
|
displayName?: string;
|
||||||
|
labelKo?: string;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ControlCondition {
|
||||||
|
field: string;
|
||||||
|
operator: string;
|
||||||
|
value: string;
|
||||||
|
logicalOperator?: string;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DataflowAction {
|
||||||
|
actionType: string;
|
||||||
|
targetTable?: string;
|
||||||
|
name?: string;
|
||||||
|
fieldMappings?: any[];
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
interface DataflowVisualizationProps {
|
interface DataflowVisualizationProps {
|
||||||
state: Partial<DataConnectionState> & {
|
state: {
|
||||||
dataflowActions?: Array<{
|
fromTable?: TableRef;
|
||||||
actionType: string;
|
toTable?: TableRef;
|
||||||
targetTable?: string;
|
controlConditions?: ControlCondition[];
|
||||||
name?: string;
|
dataflowActions?: DataflowAction[];
|
||||||
fieldMappings?: any[];
|
fromColumns?: ColumnRef[];
|
||||||
}>;
|
toColumns?: ColumnRef[];
|
||||||
|
[key: string]: any;
|
||||||
};
|
};
|
||||||
onEdit: (step: "source" | "conditions" | "actions") => void;
|
onEdit: (step: "source" | "conditions" | "actions") => void;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -832,14 +832,14 @@ function ReviewPanel({
|
||||||
}: ReviewPanelProps) {
|
}: ReviewPanelProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="flex flex-col rounded-lg border-2 border-dashed border-primary/40 bg-primary/10/50"
|
className="flex flex-col rounded-lg border-2 border-dashed border-primary/40 bg-primary/5"
|
||||||
style={{
|
style={{
|
||||||
width: "200px",
|
width: "200px",
|
||||||
maxHeight: "300px",
|
maxHeight: "300px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* 헤더 */}
|
{/* 헤더 */}
|
||||||
<div className="flex items-center gap-2 border-b border-primary/20 bg-primary/10/50 px-3 py-2 rounded-t-lg">
|
<div className="flex items-center gap-2 border-b border-primary/20 bg-primary/5 px-3 py-2 rounded-t-lg">
|
||||||
<AlertTriangle className="h-4 w-4 text-primary" />
|
<AlertTriangle className="h-4 w-4 text-primary" />
|
||||||
<span className="text-xs font-semibold text-primary">
|
<span className="text-xs font-semibold text-primary">
|
||||||
검토 필요 ({components.length}개)
|
검토 필요 ({components.length}개)
|
||||||
|
|
@ -859,7 +859,7 @@ function ReviewPanel({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 안내 문구 */}
|
{/* 안내 문구 */}
|
||||||
<div className="border-t border-primary/20 px-3 py-2 bg-primary/10/80 rounded-b-lg">
|
<div className="border-t border-primary/20 px-3 py-2 bg-primary/10 rounded-b-lg">
|
||||||
<p className="text-[10px] text-primary leading-tight">
|
<p className="text-[10px] text-primary leading-tight">
|
||||||
자동 배치됨. 클릭하여 확인 후 편집 가능
|
자동 배치됨. 클릭하여 확인 후 편집 가능
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -953,7 +953,7 @@ function HiddenPanel({
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col rounded-lg border-2 border-dashed bg-muted/50 transition-colors",
|
"flex flex-col rounded-lg border-2 border-dashed bg-muted/50 transition-colors",
|
||||||
isOver && canDrop
|
isOver && canDrop
|
||||||
? "border-input bg-muted/80/70"
|
? "border-input bg-muted/70"
|
||||||
: "border-input"
|
: "border-input"
|
||||||
)}
|
)}
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -962,7 +962,7 @@ function HiddenPanel({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* 헤더 */}
|
{/* 헤더 */}
|
||||||
<div className="flex items-center gap-2 border-b border-input bg-muted/80/50 px-3 py-2 rounded-t-lg">
|
<div className="flex items-center gap-2 border-b border-input bg-muted/50 px-3 py-2 rounded-t-lg">
|
||||||
<EyeOff className="h-4 w-4 text-muted-foreground" />
|
<EyeOff className="h-4 w-4 text-muted-foreground" />
|
||||||
<span className="text-xs font-semibold text-foreground">
|
<span className="text-xs font-semibold text-foreground">
|
||||||
숨김 ({components.length}개)
|
숨김 ({components.length}개)
|
||||||
|
|
|
||||||
|
|
@ -261,11 +261,11 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
|
||||||
label: component.title || "데이터 테이블",
|
label: component.title || "데이터 테이블",
|
||||||
tableName: component.tableName,
|
tableName: component.tableName,
|
||||||
columns: component.columns.map((col) => ({
|
columns: component.columns.map((col) => ({
|
||||||
columnName: col.field,
|
columnName: col.columnName,
|
||||||
columnLabel: col.label,
|
columnLabel: col.label,
|
||||||
inputType: col.inputType || "text",
|
inputType: col.widgetType || "text",
|
||||||
visible: col.visible !== false,
|
visible: col.visible !== false,
|
||||||
width: col.width || 150,
|
width: (col as any).width || 150,
|
||||||
sortable: col.sortable,
|
sortable: col.sortable,
|
||||||
filterable: col.filterable !== false,
|
filterable: col.filterable !== false,
|
||||||
})),
|
})),
|
||||||
|
|
@ -583,6 +583,15 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
|
||||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
||||||
const [isDeleting, setIsDeleting] = useState(false);
|
const [isDeleting, setIsDeleting] = useState(false);
|
||||||
|
|
||||||
|
// 추가/수정 모달 상태
|
||||||
|
const [showAddModal, setShowAddModal] = useState(false);
|
||||||
|
const [showEditModal, setShowEditModal] = useState(false);
|
||||||
|
const [addFormData, setAddFormData] = useState<Record<string, any>>({});
|
||||||
|
const [editFormData, setEditFormData] = useState<Record<string, any>>({});
|
||||||
|
const [editingRowData, setEditingRowData] = useState<Record<string, any> | null>(null);
|
||||||
|
const [isAdding, setIsAdding] = useState(false);
|
||||||
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
|
||||||
// 현재 사용자 정보
|
// 현재 사용자 정보
|
||||||
const [currentUser, setCurrentUser] = useState<UserInfo | null>(null);
|
const [currentUser, setCurrentUser] = useState<UserInfo | null>(null);
|
||||||
|
|
||||||
|
|
@ -696,12 +705,12 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
|
||||||
const sampleData = Array.from({ length: 3 }, (_, i) => {
|
const sampleData = Array.from({ length: 3 }, (_, i) => {
|
||||||
const sample: Record<string, any> = { id: i + 1 };
|
const sample: Record<string, any> = { id: i + 1 };
|
||||||
component.columns.forEach((col) => {
|
component.columns.forEach((col) => {
|
||||||
if (col.type === "number") {
|
if (col.widgetType === "number") {
|
||||||
sample[col.key] = Math.floor(Math.random() * 1000);
|
sample[col.columnName] = Math.floor(Math.random() * 1000);
|
||||||
} else if (col.type === "boolean") {
|
} else if (col.widgetType === "boolean") {
|
||||||
sample[col.key] = i % 2 === 0 ? "Y" : "N";
|
sample[col.columnName] = i % 2 === 0 ? "Y" : "N";
|
||||||
} else {
|
} else {
|
||||||
sample[col.key] = `샘플 ${col.label} ${i + 1}`;
|
sample[col.columnName] = `샘플 ${col.label} ${i + 1}`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return sample;
|
return sample;
|
||||||
|
|
@ -1240,6 +1249,13 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
|
||||||
}));
|
}));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleAddFormChange = useCallback((columnName: string, value: any) => {
|
||||||
|
setAddFormData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[columnName]: value,
|
||||||
|
}));
|
||||||
|
}, []);
|
||||||
|
|
||||||
// 파일 업로드 핸들러
|
// 파일 업로드 핸들러
|
||||||
const handleFileUpload = useCallback(
|
const handleFileUpload = useCallback(
|
||||||
async (columnName: string, files: FileList | null, isEdit: boolean = false) => {
|
async (columnName: string, files: FileList | null, isEdit: boolean = false) => {
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,8 @@ import {
|
||||||
Keyboard,
|
Keyboard,
|
||||||
Equal,
|
Equal,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { ScreenResolution, SCREEN_RESOLUTIONS } from "@/types/screen";
|
import { ScreenResolution } from "@/types/screen";
|
||||||
|
import { SCREEN_RESOLUTIONS } from "@/types/screen-management";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,65 @@
|
||||||
import React, { useState, useCallback, useRef, useEffect } from "react";
|
import React, { useState, useCallback, useRef, useEffect } from "react";
|
||||||
import { Upload, X, File, Image, Eye, Download, AlertCircle, CheckCircle, Loader2 } from "lucide-react";
|
import { Upload, X, File, Image, Eye, Download, AlertCircle, CheckCircle, Loader2 } from "lucide-react";
|
||||||
import { FileComponent, AttachedFileInfo } from "@/types/screen";
|
import { FileComponent } from "@/types/screen-management";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { apiClient } from "@/lib/api/client";
|
import { apiClient } from "@/lib/api/client";
|
||||||
import { useAuth } from "@/hooks/useAuth";
|
import { useAuth } from "@/hooks/useAuth";
|
||||||
|
|
||||||
|
// 첨부파일 정보 (attach_file_info 테이블 기반, UI 확장 속성 포함)
|
||||||
|
export interface AttachedFileInfo {
|
||||||
|
objid: string;
|
||||||
|
savedFileName: string;
|
||||||
|
realFileName: string;
|
||||||
|
fileSize: number;
|
||||||
|
fileExt: string;
|
||||||
|
filePath: string;
|
||||||
|
docType: string;
|
||||||
|
docTypeName: string;
|
||||||
|
targetObjid: string;
|
||||||
|
parentTargetObjid?: string;
|
||||||
|
companyCode: string;
|
||||||
|
writer: string;
|
||||||
|
regdate: string;
|
||||||
|
status: string;
|
||||||
|
uploadProgress?: number;
|
||||||
|
isUploading?: boolean;
|
||||||
|
hasError?: boolean;
|
||||||
|
errorMessage?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileTypeConfig 확장 (FileComponent에서 사용하는 추가 속성 포함)
|
||||||
|
interface ExtendedFileTypeConfig {
|
||||||
|
accept?: string[];
|
||||||
|
multiple?: boolean;
|
||||||
|
maxSize?: number;
|
||||||
|
maxFiles?: number;
|
||||||
|
showPreview?: boolean;
|
||||||
|
showProgress?: boolean;
|
||||||
|
docType?: string;
|
||||||
|
docTypeName?: string;
|
||||||
|
dragDropText?: string;
|
||||||
|
uploadButtonText?: string;
|
||||||
|
autoUpload?: boolean;
|
||||||
|
chunkedUpload?: boolean;
|
||||||
|
linkedTable?: string;
|
||||||
|
linkedField?: string;
|
||||||
|
autoLink?: boolean;
|
||||||
|
companyCode?: string;
|
||||||
|
recordId?: string;
|
||||||
|
isVirtualFileColumn?: boolean;
|
||||||
|
columnName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileComponent를 AttachedFileInfo 기반으로 확장한 컴포넌트 타입
|
||||||
|
interface FileComponentWithAttached extends Omit<FileComponent, "uploadedFiles" | "fileConfig"> {
|
||||||
|
uploadedFiles?: AttachedFileInfo[];
|
||||||
|
fileConfig: ExtendedFileTypeConfig;
|
||||||
|
}
|
||||||
|
|
||||||
interface FileUploadProps {
|
interface FileUploadProps {
|
||||||
component: FileComponent;
|
component: FileComponentWithAttached;
|
||||||
onUpdateComponent?: (updates: Partial<FileComponent>) => void;
|
onUpdateComponent?: (updates: Partial<FileComponentWithAttached>) => void;
|
||||||
onFileUpload?: (files: AttachedFileInfo[]) => void; // 파일 업로드 완료 콜백
|
onFileUpload?: (files: AttachedFileInfo[]) => void; // 파일 업로드 완료 콜백
|
||||||
userInfo?: any; // 사용자 정보 (선택적)
|
userInfo?: any; // 사용자 정보 (선택적)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue