Skip to content
This repository was archived by the owner on Mar 18, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions src/app/chat/components/ChatArea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { FiClock } from 'react-icons/fi';
import styles from '@/app/chat/assets/ChatInterface.module.scss';
import { MessageList } from './MessageList';
import { EmptyState } from './EmptyState';
import { IOLog, Workflow } from './types';

interface ChatAreaProps {
mode: "existing" | "new-workflow" | "new-default";
loading: boolean;
ioLogs: IOLog[];
workflow: Workflow;
executing: boolean;
setInputMessage: (message: string) => void;
messagesRef: React.RefObject<HTMLDivElement | null>;
pendingLogId: string | null;
renderMessageContent: (content: string, isUserMessage?: boolean) => React.ReactNode;
formatDate: (dateString: string) => string;
}

export const ChatArea: React.FC<ChatAreaProps> = ({
mode,
loading,
ioLogs,
workflow,
executing,
setInputMessage,
messagesRef,
pendingLogId,
renderMessageContent,
formatDate,
}) => {
// 1. 로딩 상태 처리 (existing 모드 전용)
if (mode === 'existing' && loading) {
return (
<div className={styles.chatContainer}>
<div className={styles.loadingState}>
<div className={styles.loadingSpinner}></div>
<p>채팅 기록을 불러오는 중...</p>
</div>
</div>
);
}

// 2. 각 모드에 맞는 컨텐츠 렌더링
const renderContent = () => {
if (mode === 'existing') {
return ioLogs.length === 0 ? (
<EmptyState title="대화 기록이 없습니다">
<p>&quot;{workflow.name}&quot; 워크플로우의 이전 대화를 불러올 수 없습니다.</p>
<p>새로운 대화를 시작해보세요.</p>
</EmptyState>
) : (
<MessageList
ioLogs={ioLogs}
pendingLogId={pendingLogId}
executing={executing}
renderMessageContent={renderMessageContent}
formatDate={formatDate}
/>
);
}

if (mode === 'new-workflow') {
return (
<EmptyState
title="첫 대화를 시작해보세요!"
showSuggestions
onChipClick={setInputMessage}
disabled={executing}
>
<p>&quot;{workflow.name}&quot; 워크플로우가 준비되었습니다.</p>
</EmptyState>
);
}

if (mode === 'new-default') {
return (
<EmptyState
title="첫 대화를 시작해보세요!"
showSuggestions
onChipClick={setInputMessage}
disabled={executing}
>
<p>일반 채팅 모드로 자유롭게 대화할 수 있습니다.</p>
</EmptyState>
);
}

return null; // 예외 케이스
};

return (
<div className={styles.chatContainer}>
<div ref={messagesRef} className={styles.messagesArea}>
{renderContent()}
</div>
</div>
);
};
69 changes: 28 additions & 41 deletions src/app/chat/components/ChatContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import { LuWorkflow } from "react-icons/lu";
import { IoChatbubblesOutline } from "react-icons/io5";
import WorkflowSelection from './WorkflowSelection';
import ChatInterface from './ChatInterface';
import NewChatInterface from './NewChatInterface';
import DefaultChatInterface from './DefaultChatInterface';

interface ChatContentProps {
onChatStarted?: () => void; // 채팅 시작 후 호출될 콜백
Expand Down Expand Up @@ -49,57 +47,46 @@ const ChatContentInner: React.FC<ChatContentProps> = ({ onChatStarted }) => {

const handleWorkflowSelect = (workflow: any) => {
setSelectedWorkflow(workflow);
// 새로운 채팅으로 시작 (항상 NewChatInterface 사용)
setCurrentView('newChat');
};

const handleDefaultChatStart = () => {
setSelectedWorkflow({
id: 'default_mode',
name: 'default_mode',
filename: 'default_chat',
author: 'System',
nodeCount: 1,
status: 'active' as const,
});
setCurrentView('defaultChat');
};

// 일반 채팅 화면 (DefaultChatInterface)
if (currentView === 'defaultChat') {
return (
<div className={styles.chatContainer}>
<div className={styles.workflowSection}>
<DefaultChatInterface
onBack={() => setCurrentView('welcome')}
onChatStarted={onChatStarted}
/>
</div>
</div>
);
}
const getChatMode = () => {
if (currentView === 'existingChat' && selectedWorkflow) return 'existing';
if (currentView === 'newChat'&& selectedWorkflow) return 'new-workflow';
if (currentView === 'defaultChat') return 'new-default';
return null;
};

// 새로운 채팅 화면 (NewChatInterface)
if (currentView === 'newChat' && selectedWorkflow) {
return (
<div className={styles.chatContainer}>
<div className={styles.workflowSection}>
<NewChatInterface
workflow={selectedWorkflow}
onBack={() => setCurrentView('workflow')}
onChatStarted={onChatStarted}
/>
</div>
</div>
);
}
const chatMode = getChatMode();

// 기존 채팅 화면 (ChatInterface)
if (currentView === 'existingChat' && selectedWorkflow) {
if (chatMode) {
return (
<div className={styles.chatContainer}>
<div className={styles.workflowSection}>
<ChatInterface
workflow={selectedWorkflow}
existingChatData={existingChatData}
onBack={() => setCurrentView('workflow')}
/>
</div>
<div className="h-full">
{chatMode && (
<ChatInterface
key={chatMode === 'existing' ? existingChatData?.interactionId : chatMode}
mode={chatMode}
workflow={selectedWorkflow}
existingChatData={chatMode === 'existing' ? existingChatData : undefined}
onChatStarted={chatMode === 'existing' ? undefined : onChatStarted}
onBack={currentView === 'defaultChat' ? () => setCurrentView('welcome') : () => setCurrentView('workflow')}
/>
)}
</div>
);
}
};

// 워크플로우 선택 화면
if (currentView === 'workflow') {
Expand Down
49 changes: 49 additions & 0 deletions src/app/chat/components/ChatHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { FiArrowLeft, FiMessageSquare } from 'react-icons/fi';
import styles from '@/app/chat/assets/ChatInterface.module.scss';
import { ChatHeaderProps } from './types';

const ChatHeader: React.FC<ChatHeaderProps> = ({ mode, workflow, ioLogs, onBack, hideBackButton }) => {
let title = '';
let subtitle = '';
let chatCountText = '';

const isExistingMode = mode === 'existing';

if (isExistingMode) {
title = workflow.name === 'default_mode' ? '일반 채팅' : workflow.name;
subtitle = hideBackButton ? '현재 채팅을 계속하세요' : '기존 대화를 계속하세요';
chatCountText = `${ioLogs.length}개의 대화`;
} else if (mode === 'new-default') {
title = '일반 채팅';
subtitle = '자유롭게 대화를 시작하세요';
chatCountText = '새 채팅';
} else {
title = workflow.name;
subtitle = '새로운 대화를 시작하세요';
chatCountText = '새 채팅';
}

const showBackButton = (!isExistingMode || !hideBackButton);

return (
<div className={styles.header}>
<div className={styles.headerInfo}>
{showBackButton && (
<button className={styles.backButton} onClick={onBack}>
<FiArrowLeft />
</button>
)}
<div>
<h2>{title}</h2>
<p>{subtitle}</p>
</div>
</div>
<div className={styles.chatCount}>
<FiMessageSquare />
<span>{chatCountText}</span>
</div>
</div>
);
};

export default ChatHeader;
Loading