From b09a4172915c4e2b806c523ef2d5b5de520a3b85 Mon Sep 17 00:00:00 2001 From: sheetEasy AI Team Date: Fri, 27 Jun 2025 14:47:47 +0900 Subject: [PATCH] =?UTF-8?q?=ED=9E=88=EC=8A=A4=ED=86=A0=EB=A6=AC=20?= =?UTF-8?q?=ED=8C=A8=EB=84=90=20=EC=9E=91=EC=97=85=20=EC=99=84=EB=A3=8C.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/sheet/EditSheetViewer.tsx | 155 ++++++++++++++++++++++- src/components/sheet/PromptInput.tsx | 22 ++-- src/components/ui/historyPanel.tsx | 2 +- vite.config.ts | 3 +- 4 files changed, 164 insertions(+), 18 deletions(-) diff --git a/src/components/sheet/EditSheetViewer.tsx b/src/components/sheet/EditSheetViewer.tsx index 576a4c2..abceae0 100644 --- a/src/components/sheet/EditSheetViewer.tsx +++ b/src/components/sheet/EditSheetViewer.tsx @@ -8,7 +8,6 @@ import { } from "@univerjs/presets"; import { UniverSheetsCorePreset } from "@univerjs/presets/preset-sheets-core"; import UniverPresetSheetsCoreEnUS from "@univerjs/presets/preset-sheets-core/locales/en-US"; -import { UniverInstanceType } from "@univerjs/core"; // Presets CSS import import "@univerjs/presets/lib/styles/preset-sheets-core.css"; @@ -327,7 +326,7 @@ const TestSheetViewer: React.FC = () => { const fileInputRef = useRef(null); const mountedRef = useRef(false); - const [isInitialized, setIsInitialized] = useState(false); + const [, setIsInitialized] = useState(false); const [showUploadOverlay, setShowUploadOverlay] = useState(true); const [isDragOver, setIsDragOver] = useState(false); const [isProcessing, setIsProcessing] = useState(false); @@ -355,12 +354,73 @@ const TestSheetViewer: React.FC = () => { } }; - const handleHistoryReapply = (entry: HistoryEntry) => { - // 히스토리 항목 재적용 로직 + const handleHistoryReapply = useCallback(async (entry: HistoryEntry) => { + console.log("🔄 히스토리 재적용 시작:", entry); + + // 1. 프롬프트 입력창에 기존 프롬프트 설정 setPrompt(entry.prompt); - console.log("🔄 히스토리 재적용:", entry); - // TODO: 실제 액션 재실행 로직 구현 - }; + + // 2. 확인 대화상자 + const confirmReapply = window.confirm( + `다음 프롬프트를 다시 실행하시겠습니까?\n\n"${entry.prompt}"\n\n범위: ${entry.range} | 시트: ${entry.sheetName}`, + ); + + if (!confirmReapply) { + return; + } + + // 3. 재적용 히스토리 항목 생성 + const reapplyHistoryId = addHistoryEntry( + `[재적용] ${entry.prompt}`, + entry.range, + entry.sheetName, + [], + "pending", + ); + + try { + // 4. AI 프로세서 실행 + const result = await aiProcessor.processPrompt(entry.prompt, true); + + // 5. 결과에 따라 히스토리 업데이트 + if (result.success) { + updateHistoryEntry(reapplyHistoryId, { + status: "success", + actions: + result.appliedCells?.map((cell) => ({ + type: "formula" as const, + range: cell, + formula: `=재적용 수식`, // TODO: 실제 수식 정보 저장 + })) || [], + }); + + // 성공 시 팝업 제거 (히스토리 패널에서 확인 가능) + console.log(`✅ 재적용 성공: ${result.message}`); + } else { + updateHistoryEntry(reapplyHistoryId, { + status: "error", + error: result.message, + actions: [], + }); + + // 실패 알림 + alert(`❌ 재적용 실패: ${result.message}`); + } + } catch (error) { + // 6. 예외 처리 + const errorMessage = + error instanceof Error + ? error.message + : "알 수 없는 오류가 발생했습니다."; + updateHistoryEntry(reapplyHistoryId, { + status: "error", + error: errorMessage, + actions: [], + }); + + alert(`❌ 재적용 중 오류: ${errorMessage}`); + } + }, []); // 새 히스토리 항목 추가 함수 const addHistoryEntry = ( @@ -383,8 +443,87 @@ const TestSheetViewer: React.FC = () => { }; setHistory((prev) => [newEntry, ...prev]); // 최신 항목을 맨 위에 + return newEntry.id; // 히스토리 ID 반환하여 나중에 업데이트 가능 }; + // 기존 히스토리 항목 업데이트 함수 + const updateHistoryEntry = ( + id: string, + updates: Partial>, + ) => { + setHistory((prev) => + prev.map((entry) => (entry.id === id ? { ...entry, ...updates } : entry)), + ); + }; + + // AI 프롬프트 실행 핸들러 (히스토리 연동) + const handlePromptExecute = useCallback(async () => { + if (!prompt.trim()) { + alert("프롬프트를 입력해주세요."); + return; + } + + const currentRange = appStore.selectedRange + ? rangeToAddress(appStore.selectedRange.range) + : "A1"; + const currentSheetName = "Sheet1"; // TODO: 실제 시트명 가져오기 + + // 1. 히스토리에 pending 상태로 추가 + const historyId = addHistoryEntry( + prompt.trim(), + currentRange, + currentSheetName, + [], + "pending", + ); + + try { + // 2. AI 프로세서 실행 + const result = await aiProcessor.processPrompt(prompt.trim(), true); + + // 3. 결과에 따라 히스토리 업데이트 + if (result.success) { + updateHistoryEntry(historyId, { + status: "success", + actions: + result.appliedCells?.map((cell) => ({ + type: "formula" as const, + range: cell, + formula: `=수식`, // TODO: 실제 수식 정보 저장 + })) || [], + }); + + // 성공 시 팝업 제거 (히스토리 패널에서 확인 가능) + console.log(`✅ ${result.message}`); + } else { + updateHistoryEntry(historyId, { + status: "error", + error: result.message, + actions: [], + }); + + // 실패 알림 + alert(`❌ ${result.message}`); + } + } catch (error) { + // 4. 예외 처리 + const errorMessage = + error instanceof Error + ? error.message + : "알 수 없는 오류가 발생했습니다."; + updateHistoryEntry(historyId, { + status: "error", + error: errorMessage, + actions: [], + }); + + alert(`❌ 처리 중 오류: ${errorMessage}`); + } + + // 5. 프롬프트 입력창 초기화 + setPrompt(""); + }, [prompt, appStore.selectedRange]); + // Univer 초기화 함수 (Presets 기반) const initializeUniver = useCallback( async (workbookData?: any) => { @@ -769,8 +908,10 @@ const TestSheetViewer: React.FC = () => { setPrompt(e.target.value)} + onExecute={handlePromptExecute} onHistoryToggle={handleHistoryToggle} historyCount={history.length} + disabled={isProcessing} /> {/* 히스토리 패널 - 파일이 업로드된 후에만 표시 */} diff --git a/src/components/sheet/PromptInput.tsx b/src/components/sheet/PromptInput.tsx index 117b87c..3af9efd 100644 --- a/src/components/sheet/PromptInput.tsx +++ b/src/components/sheet/PromptInput.tsx @@ -24,18 +24,16 @@ const PromptInput: React.FC = ({ value, onChange, onExecute, - disabled = true, + disabled: _disabled = true, maxLength = 500, onHistoryToggle, historyCount, }) => { const textareaRef = useRef(null); - const [showCellInsertFeedback, setShowCellInsertFeedback] = useState(false); - const [lastInsertedCell, setLastInsertedCell] = useState(null); - const [currentSelectedCell, setCurrentSelectedCell] = useState( - null, - ); - const [processingMessage, setProcessingMessage] = useState(""); + const [, setShowCellInsertFeedback] = useState(false); + const [, setLastInsertedCell] = useState(null); + const [, setCurrentSelectedCell] = useState(null); + const [, setProcessingMessage] = useState(""); const cellAddressToInsert = useAppStore((state) => state.cellAddressToInsert); const setCellAddressToInsert = useAppStore( @@ -53,7 +51,7 @@ const PromptInput: React.FC = ({ }, [cellAddressToInsert]); /** - * 전송하기 버튼 클릭 핸들러 + * 전송하기 버튼 클릭 핸들러 - 상위 컴포넌트 onExecute 사용 또는 기본 로직 */ const handleExecute = async () => { if (!value.trim()) { @@ -66,6 +64,14 @@ const PromptInput: React.FC = ({ return; } + // 상위 컴포넌트에서 onExecute를 전달받은 경우 해당 함수 사용 + if (onExecute) { + console.log("🚀 상위 컴포넌트 onExecute 함수 호출"); + await onExecute(); + return; + } + + // 폴백: 기본 AI 프로세서 직접 호출 setProcessingMessage("AI가 요청을 처리하고 있습니다..."); try { diff --git a/src/components/ui/historyPanel.tsx b/src/components/ui/historyPanel.tsx index e351c4d..051bd18 100644 --- a/src/components/ui/historyPanel.tsx +++ b/src/components/ui/historyPanel.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Card, CardContent, CardHeader, CardTitle } from "./card"; +import { Card, CardContent, CardHeader } from "./card"; import { Button } from "./button"; import type { HistoryPanelProps, HistoryEntry } from "../../types/ai"; import { cn } from "../../lib/utils"; diff --git a/vite.config.ts b/vite.config.ts index 5d566b2..c181bf4 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -41,7 +41,6 @@ export default defineConfig({ "@univerjs/sheets-formula-ui", "@univerjs/sheets-numfmt", "@univerjs/sheets-numfmt-ui", - "@univerjs/facade", ], }, @@ -69,7 +68,7 @@ export default defineConfig({ "@univerjs/sheets-numfmt-ui", ], "univer-docs": ["@univerjs/docs", "@univerjs/docs-ui"], - "univer-ui": ["@univerjs/ui", "@univerjs/facade"], + "univer-ui": ["@univerjs/ui"], }, }, },