feat: 튜토리얼 UX 개선 - 네비게이션 및 프롬프트 피드백 강화
🔧 주요 개선사항: 1. Topbar 네비게이션 문제 해결 - 튜토리얼 페이지에서 메뉴 항목 클릭 시 올바른 라우팅 구현 - Tutorial 메뉴 클릭 시 페이지 리로드 기능 추가 (컴포넌트 리마운트) - 라우팅 우선, 스크롤 폴백 패턴 적용 2. PromptInput 플레이스홀더 개선 - 튜토리얼 실행 후 실제 사용된 프롬프트를 플레이스홀더에 표시 - 명확한 프롬프트 → 실행 → 결과 추적 가능 - 새 튜토리얼 선택 시 이전 프롬프트 초기화 3. 새로운 튜토리얼 시스템 구축 - TutorialSheetViewer: 단계별 튜토리얼 플로우 구현 - TutorialCard: 개별 튜토리얼 카드 컴포넌트 - TutorialExecutor: 튜토리얼 실행 엔진 - TutorialDataGenerator: 10개 Excel 함수 데이터 생성 📁 변경된 파일들: - src/App.tsx: 네비게이션 핸들러 추가 - src/components/ui/topbar.tsx: 라우팅 기반 네비게이션 구현 - src/components/sheet/PromptInput.tsx: 동적 플레이스홀더 추가 - src/components/TutorialSheetViewer.tsx: 튜토리얼 전용 뷰어 구현 - src/types/tutorial.ts: 튜토리얼 타입 정의 - .cursor/rules/tutorial-navigation-fix.mdc: 구현 패턴 문서화 ✅ 검증 완료: - 모든 topbar 메뉴 정상 네비게이션 - 튜토리얼 페이지 리로드 기능 작동 - 실행된 프롬프트 플레이스홀더 표시 - AI 워크플로우 시뮬레이션 완성
This commit is contained in:
@@ -19,6 +19,7 @@ import { useAppStore } from "../../stores/useAppStore";
|
||||
import { rangeToAddress } from "../../utils/cellUtils";
|
||||
import { CellSelectionHandler } from "../../utils/cellSelectionHandler";
|
||||
import { aiProcessor } from "../../utils/aiProcessor";
|
||||
import { TutorialExecutor } from "../../utils/tutorialExecutor";
|
||||
import type { HistoryEntry } from "../../types/ai";
|
||||
|
||||
// 전역 고유 키 생성
|
||||
@@ -74,7 +75,7 @@ const getGlobalState = (): GlobalUniverState => {
|
||||
* Presets 기반 강화된 전역 Univer 관리자
|
||||
* 공식 문서 권장사항에 따라 createUniver 사용
|
||||
*/
|
||||
const UniverseManager = {
|
||||
export const UniverseManager = {
|
||||
// 전역 인스턴스 생성 (완전 단일 인스턴스 보장)
|
||||
async createInstance(container: HTMLElement): Promise<any> {
|
||||
const state = getGlobalState();
|
||||
@@ -339,6 +340,9 @@ const TestSheetViewer: React.FC = () => {
|
||||
// CellSelectionHandler 인스턴스 생성
|
||||
const cellSelectionHandler = useRef(new CellSelectionHandler());
|
||||
|
||||
// TutorialExecutor 인스턴스 생성
|
||||
const tutorialExecutor = useRef(new TutorialExecutor());
|
||||
|
||||
// 히스토리 관련 상태 추가
|
||||
const [isHistoryOpen, setIsHistoryOpen] = useState(false);
|
||||
const [history, setHistory] = useState<HistoryEntry[]>([]);
|
||||
@@ -587,6 +591,9 @@ const TestSheetViewer: React.FC = () => {
|
||||
// 셀 선택 핸들러 초기화 - SRP에 맞춰 별도 클래스로 분리
|
||||
cellSelectionHandler.current.initialize(univer);
|
||||
|
||||
// TutorialExecutor에 Univer API 설정
|
||||
tutorialExecutor.current.setUniverAPI(univerAPI);
|
||||
|
||||
setIsInitialized(true);
|
||||
} else {
|
||||
console.warn("⚠️ univerAPI가 제공되지 않음");
|
||||
@@ -862,6 +869,92 @@ const TestSheetViewer: React.FC = () => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
// 튜토리얼 자동 실행 로직
|
||||
useEffect(() => {
|
||||
const { tutorialSession } = appStore;
|
||||
|
||||
// 튜토리얼이 선택되었고 아직 실행되지 않은 경우
|
||||
if (
|
||||
tutorialSession.activeTutorial &&
|
||||
tutorialSession.execution?.status === "준비중" &&
|
||||
tutorialSession.isAutoMode &&
|
||||
!tutorialExecutor.current.isCurrentlyExecuting()
|
||||
) {
|
||||
console.log(
|
||||
"🎯 튜토리얼 자동 실행 시작:",
|
||||
tutorialSession.activeTutorial.metadata.title,
|
||||
);
|
||||
|
||||
const executeTutorial = async () => {
|
||||
try {
|
||||
// 상태를 실행중으로 업데이트
|
||||
appStore.updateTutorialExecution("실행중", 1);
|
||||
|
||||
// 튜토리얼 실행
|
||||
const result = await tutorialExecutor.current.startTutorial(
|
||||
tutorialSession.activeTutorial!,
|
||||
{
|
||||
autoExecute: true,
|
||||
stepDelay: 1500,
|
||||
highlightDuration: 2000,
|
||||
showFormula: true,
|
||||
enableAnimation: true,
|
||||
},
|
||||
);
|
||||
|
||||
if (result.success) {
|
||||
// 성공 시 상태 업데이트
|
||||
appStore.updateTutorialExecution("완료", 3);
|
||||
|
||||
// 프롬프트 자동 입력
|
||||
if (tutorialSession.activeTutorial!.prompt) {
|
||||
setPrompt(tutorialSession.activeTutorial!.prompt);
|
||||
setShowPromptInput(true);
|
||||
}
|
||||
|
||||
console.log("✅ 튜토리얼 실행 완료:", result);
|
||||
} else {
|
||||
appStore.updateTutorialExecution(
|
||||
"오류",
|
||||
undefined,
|
||||
"튜토리얼 실행 실패",
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ 튜토리얼 실행 오류:", error);
|
||||
appStore.updateTutorialExecution(
|
||||
"오류",
|
||||
undefined,
|
||||
error instanceof Error ? error.message : "알 수 없는 오류",
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Univer가 초기화된 후 실행
|
||||
if (getGlobalState().univerAPI) {
|
||||
executeTutorial();
|
||||
} else {
|
||||
// Univer API가 준비될 때까지 대기
|
||||
const checkInterval = setInterval(() => {
|
||||
if (getGlobalState().univerAPI) {
|
||||
clearInterval(checkInterval);
|
||||
executeTutorial();
|
||||
}
|
||||
}, 500);
|
||||
|
||||
// 10초 후 타임아웃
|
||||
setTimeout(() => {
|
||||
clearInterval(checkInterval);
|
||||
appStore.updateTutorialExecution(
|
||||
"오류",
|
||||
undefined,
|
||||
"Univer API 초기화 타임아웃",
|
||||
);
|
||||
}, 10000);
|
||||
}
|
||||
}
|
||||
}, [appStore.tutorialSession, appStore]);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex flex-col relative">
|
||||
{/* 헤더 - App.tsx와 동일한 스타일 적용 */}
|
||||
|
||||
Reference in New Issue
Block a user