import React, { useState, useRef, useEffect } from "react";
import ChatArea from "../components/ChatArea";
import SessionSidebar from "../components/SessionSidebar";
import useCodeAssistant from "../hooks/useCodeAssistant";
import { updateSession } from "../services/apiService";
const CodeAssistantPage = () => {
const pageContainerRef = useRef(null);
const {
chatHistory,
isProcessing,
errorMessage,
showErrorModal,
tokenUsage,
handleSendChat,
setShowErrorModal,
handleSwitchSession,
sessionId,
userConfigData,
localActiveLLM,
setLocalActiveLLM,
isConfigured,
missingConfigs
} = useCodeAssistant({ pageContainerRef });
const [showConfigModal, setShowConfigModal] = useState(false);
const [sidebarRefreshTick, setSidebarRefreshTick] = useState(0);
const handleSaveQuickConfig = async () => {
try {
if (sessionId && localActiveLLM) {
await updateSession(sessionId, { provider_name: localActiveLLM });
setSidebarRefreshTick(t => t + 1);
}
setShowConfigModal(false);
} catch (e) {
console.error("Failed to update session configs:", e);
}
};
useEffect(() => {
if (pageContainerRef.current) {
pageContainerRef.current.scrollTop = pageContainerRef.current.scrollHeight;
}
}, [chatHistory]);
return (
<div className="flex flex-col flex-grow h-full overflow-hidden bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-gray-100 relative">
<SessionSidebar
featureName="coding_assistant"
currentSessionId={sessionId}
onSwitchSession={handleSwitchSession}
onNewSession={() => handleSendChat("/new")}
refreshTick={sidebarRefreshTick}
/>
{/* Main content area */}
<div className="flex-grow flex flex-col px-4 min-h-0" ref={pageContainerRef}>
<div className="max-w-4xl mx-auto w-full h-full pt-12 pb-8 flex flex-col min-h-0">
{/* Chat Area */}
<div className="flex flex-col h-full min-h-0">
<div className="flex-grow p-4 bg-white dark:bg-gray-800 rounded-xl shadow-lg border border-gray-200 dark:border-gray-700 flex flex-col min-h-0">
<h2 className="text-xl font-bold mb-4 flex justify-between items-center border-b border-gray-100 dark:border-gray-700 pb-4">
<div className="flex items-center gap-3">
<div className="p-2 bg-indigo-100 dark:bg-indigo-900/30 rounded-lg">
<svg className="w-6 h-6 text-indigo-600 dark:text-indigo-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
</svg>
</div>
<div className="flex flex-col">
<span className="text-lg">Coding Assistant</span>
<span className="text-[10px] text-gray-500 font-medium uppercase tracking-widest">Enhanced with Skills & Prompts</span>
</div>
{!isConfigured && (
<div className="group relative flex items-center">
<svg className="w-5 h-5 text-yellow-500 cursor-pointer" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
<div className="absolute left-1/2 -translate-x-1/2 top-full mt-2 w-56 bg-gray-900 text-white text-[11px] rounded shadow-lg p-2.5 opacity-0 group-hover:opacity-100 transition-opacity z-50 pointer-events-none text-left">
<p className="font-bold mb-1 text-red-400">Missing Key</p>
<ul className="list-disc pl-3 space-y-0.5">
{missingConfigs?.map((m, i) => <li key={i}>{m}</li>)}
</ul>
</div>
</div>
)}
<button
onClick={() => setShowConfigModal(true)}
className="text-gray-400 hover:text-indigo-600 transition-colors"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path></svg>
</button>
</div>
<div className="flex items-center space-x-6">
<div className="flex flex-col items-end">
<div className="text-[10px] font-bold text-gray-400 dark:text-gray-500 uppercase tracking-widest mb-1">
Token Usage
</div>
<div className="flex items-center gap-2">
<div className="w-32 h-1.5 bg-gray-100 dark:bg-gray-700 rounded-full overflow-hidden">
<div
className={`h-full transition-all duration-700 ease-out ${tokenUsage?.percentage > 80 ? 'bg-red-500' : 'bg-indigo-500'}`}
style={{ width: `${Math.min(tokenUsage?.percentage || 0, 100)}%` }}
></div>
</div>
<span className={`text-xs font-mono font-bold ${tokenUsage?.percentage > 80 ? 'text-red-500' : 'text-gray-400'}`}>
{tokenUsage?.percentage || 0}%
</span>
</div>
</div>
<button
onClick={() => handleSendChat("/new")}
className="text-xs font-bold px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg transition-all shadow-md hover:shadow-indigo-500/20 active:scale-95"
>
+ NEW SESSION
</button>
</div>
</h2>
<div className="flex-grow flex flex-col min-h-0">
<ChatArea
chatHistory={chatHistory}
onSendMessage={handleSendChat}
isProcessing={isProcessing}
featureName="coding_assistant"
/>
</div>
</div>
</div>
</div>
</div>
{/* Error Modal */}
{showErrorModal && (
<div className="fixed inset-0 bg-gray-900/60 backdrop-blur-sm flex justify-center items-center z-50 animate-in fade-in duration-300">
<div className="bg-white dark:bg-gray-800 p-8 rounded-2xl shadow-2xl max-w-sm w-full text-center border border-red-100 dark:border-red-900/30">
<div className="w-16 h-16 bg-red-100 dark:bg-red-900/30 rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>
</div>
<h2 className="text-xl font-bold mb-2 text-gray-900 dark:text-white">Attention Required</h2>
<p className="text-gray-500 dark:text-gray-400 mb-6 text-sm">{errorMessage}</p>
<button
onClick={() => setShowErrorModal(false)}
className="w-full bg-gray-900 dark:bg-white dark:text-gray-900 text-white font-bold py-3 rounded-xl transition-all active:scale-95"
>
Understand
</button>
</div>
</div>
)}
{showConfigModal && (
<div className="fixed inset-0 bg-gray-900/40 backdrop-blur-sm flex justify-center items-center z-50">
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow-2xl w-full max-w-md overflow-hidden animate-in zoom-in-95 duration-200">
<div className="px-6 py-5 border-b border-gray-100 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50 flex justify-between items-center">
<h3 className="text-lg font-bold text-gray-900 dark:text-gray-100 flex items-center gap-2">
<svg className="w-5 h-5 text-indigo-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path></svg>
Session Engine
</h3>
<button onClick={() => setShowConfigModal(false)} className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 p-2">
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" /></svg>
</button>
</div>
<div className="p-8 space-y-6">
<div>
<label className="block text-xs font-bold text-gray-400 dark:text-gray-500 uppercase tracking-widest mb-2">Active LLM Provider</label>
<select
value={localActiveLLM}
onChange={(e) => setLocalActiveLLM(e.target.value)}
className="w-full border-2 border-gray-100 dark:border-gray-700 rounded-xl p-3 bg-white dark:bg-gray-800 text-sm font-medium focus:border-indigo-500 focus:ring-0 transition-all outline-none"
>
<option value="">-- Choose Provider --</option>
{userConfigData?.effective?.llm?.providers && Object.keys(userConfigData.effective.llm.providers).map(pid => {
const modelName = userConfigData.effective.llm.providers[pid].model;
return (
<option key={pid} value={pid}>
{pid} {modelName ? `(${modelName})` : ''}
</option>
);
})}
</select>
</div>
</div>
<div className="px-8 py-5 bg-gray-50 dark:bg-gray-800/50 border-t border-gray-100 dark:border-gray-700 flex justify-end gap-3">
<button
onClick={() => setShowConfigModal(false)}
className="px-6 py-2.5 text-sm font-bold text-gray-500 hover:text-gray-800 transition-colors"
>
Cancel
</button>
<button
onClick={handleSaveQuickConfig}
className="px-6 py-2.5 bg-indigo-600 hover:bg-indigo-700 text-white rounded-xl font-bold text-sm shadow-lg shadow-indigo-500/20 active:scale-95 transition-all"
>
Apply Changes
</button>
</div>
</div>
</div>
)}
</div>
);
};
export default CodeAssistantPage;