히스토리 패널 작업 완료.
This commit is contained in:
@@ -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<HTMLInputElement>(null);
|
||||
const mountedRef = useRef<boolean>(false);
|
||||
|
||||
const [isInitialized, setIsInitialized] = useState<boolean>(false);
|
||||
const [, setIsInitialized] = useState<boolean>(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<Omit<HistoryEntry, "id" | "timestamp">>,
|
||||
) => {
|
||||
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 = () => {
|
||||
<PromptInput
|
||||
value={prompt}
|
||||
onChange={(e) => setPrompt(e.target.value)}
|
||||
onExecute={handlePromptExecute}
|
||||
onHistoryToggle={handleHistoryToggle}
|
||||
historyCount={history.length}
|
||||
disabled={isProcessing}
|
||||
/>
|
||||
|
||||
{/* 히스토리 패널 - 파일이 업로드된 후에만 표시 */}
|
||||
|
||||
@@ -24,18 +24,16 @@ const PromptInput: React.FC<PromptInputProps> = ({
|
||||
value,
|
||||
onChange,
|
||||
onExecute,
|
||||
disabled = true,
|
||||
disabled: _disabled = true,
|
||||
maxLength = 500,
|
||||
onHistoryToggle,
|
||||
historyCount,
|
||||
}) => {
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const [showCellInsertFeedback, setShowCellInsertFeedback] = useState(false);
|
||||
const [lastInsertedCell, setLastInsertedCell] = useState<string | null>(null);
|
||||
const [currentSelectedCell, setCurrentSelectedCell] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const [processingMessage, setProcessingMessage] = useState<string>("");
|
||||
const [, setShowCellInsertFeedback] = useState(false);
|
||||
const [, setLastInsertedCell] = useState<string | null>(null);
|
||||
const [, setCurrentSelectedCell] = useState<string | null>(null);
|
||||
const [, setProcessingMessage] = useState<string>("");
|
||||
|
||||
const cellAddressToInsert = useAppStore((state) => state.cellAddressToInsert);
|
||||
const setCellAddressToInsert = useAppStore(
|
||||
@@ -53,7 +51,7 @@ const PromptInput: React.FC<PromptInputProps> = ({
|
||||
}, [cellAddressToInsert]);
|
||||
|
||||
/**
|
||||
* 전송하기 버튼 클릭 핸들러
|
||||
* 전송하기 버튼 클릭 핸들러 - 상위 컴포넌트 onExecute 사용 또는 기본 로직
|
||||
*/
|
||||
const handleExecute = async () => {
|
||||
if (!value.trim()) {
|
||||
@@ -66,6 +64,14 @@ const PromptInput: React.FC<PromptInputProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
// 상위 컴포넌트에서 onExecute를 전달받은 경우 해당 함수 사용
|
||||
if (onExecute) {
|
||||
console.log("🚀 상위 컴포넌트 onExecute 함수 호출");
|
||||
await onExecute();
|
||||
return;
|
||||
}
|
||||
|
||||
// 폴백: 기본 AI 프로세서 직접 호출
|
||||
setProcessingMessage("AI가 요청을 처리하고 있습니다...");
|
||||
|
||||
try {
|
||||
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user