refactor: move VersionUpgradeModal and collapsed sidebar to Sidebar component from App.jsx

This commit is contained in:
Haileyesus
2026-02-06 10:55:32 +03:00
parent f8d6daa37b
commit 6fad362cab
2 changed files with 86 additions and 97 deletions

View File

@@ -26,7 +26,6 @@ import MainContent from './components/MainContent';
import MobileNav from './components/MobileNav'; import MobileNav from './components/MobileNav';
import Settings from './components/Settings'; import Settings from './components/Settings';
import QuickSettingsPanel from './components/QuickSettingsPanel'; import QuickSettingsPanel from './components/QuickSettingsPanel';
import VersionUpgradeModal from './components/modals/VersionUpgradeModal';
import { ThemeProvider } from './contexts/ThemeContext'; import { ThemeProvider } from './contexts/ThemeContext';
import { AuthProvider } from './contexts/AuthContext'; import { AuthProvider } from './contexts/AuthContext';
@@ -34,7 +33,6 @@ import { TaskMasterProvider } from './contexts/TaskMasterContext';
import { TasksSettingsProvider } from './contexts/TasksSettingsContext'; import { TasksSettingsProvider } from './contexts/TasksSettingsContext';
import { WebSocketProvider, useWebSocket } from './contexts/WebSocketContext'; import { WebSocketProvider, useWebSocket } from './contexts/WebSocketContext';
import ProtectedRoute from './components/ProtectedRoute'; import ProtectedRoute from './components/ProtectedRoute';
import { useVersionCheck } from './hooks/useVersionCheck';
import useLocalStorage from './hooks/useLocalStorage'; import useLocalStorage from './hooks/useLocalStorage';
import { api, authenticatedFetch } from './utils/api'; import { api, authenticatedFetch } from './utils/api';
import { I18nextProvider, useTranslation } from 'react-i18next'; import { I18nextProvider, useTranslation } from 'react-i18next';
@@ -51,9 +49,6 @@ function AppContent() {
const renderCountRef = useRef(0); const renderCountRef = useRef(0);
// console.log(`AppContent render count: ${renderCountRef.current++}`); // console.log(`AppContent render count: ${renderCountRef.current++}`);
const { updateAvailable, latestVersion, currentVersion, releaseInfo } = useVersionCheck('siteboon', 'claudecodeui');
const [showVersionModal, setShowVersionModal] = useState(false);
const [projects, setProjects] = useState([]); const [projects, setProjects] = useState([]);
const [selectedProject, setSelectedProject] = useState(null); const [selectedProject, setSelectedProject] = useState(null);
const [selectedSession, setSelectedSession] = useState(null); const [selectedSession, setSelectedSession] = useState(null);
@@ -591,75 +586,25 @@ function AppContent() {
sidebarVisible ? 'w-80' : 'w-14' sidebarVisible ? 'w-80' : 'w-14'
}`} }`}
> >
<div className="h-full overflow-hidden"> <Sidebar
{sidebarVisible ? ( projects={projects}
<Sidebar selectedProject={selectedProject}
projects={projects} selectedSession={selectedSession}
selectedProject={selectedProject} onProjectSelect={handleProjectSelect}
selectedSession={selectedSession} onSessionSelect={handleSessionSelect}
onProjectSelect={handleProjectSelect} onNewSession={handleNewSession}
onSessionSelect={handleSessionSelect} onSessionDelete={handleSessionDelete}
onNewSession={handleNewSession} onProjectDelete={handleProjectDelete}
onSessionDelete={handleSessionDelete} isLoading={isLoadingProjects}
onProjectDelete={handleProjectDelete} loadingProgress={loadingProgress}
isLoading={isLoadingProjects} onRefresh={handleSidebarRefresh}
loadingProgress={loadingProgress} onShowSettings={() => setShowSettings(true)}
onRefresh={handleSidebarRefresh} isPWA={isPWA}
onShowSettings={() => setShowSettings(true)} isMobile={isMobile}
updateAvailable={updateAvailable} onToggleSidebar={() => setSidebarVisible(false)}
latestVersion={latestVersion} isCollapsed={!sidebarVisible}
currentVersion={currentVersion} onExpandSidebar={() => setSidebarVisible(true)}
releaseInfo={releaseInfo} />
onShowVersionModal={() => setShowVersionModal(true)}
isPWA={isPWA}
isMobile={isMobile}
onToggleSidebar={() => setSidebarVisible(false)}
/>
) : (
/* Collapsed Sidebar */
<div className="h-full flex flex-col items-center py-4 gap-4">
{/* Expand Button */}
<button
onClick={() => setSidebarVisible(true)}
className="p-2 hover:bg-accent rounded-md transition-colors duration-200 group"
aria-label={t('versionUpdate.ariaLabels.showSidebar')}
title={t('versionUpdate.ariaLabels.showSidebar')}
>
<svg
className="w-5 h-5 text-foreground group-hover:scale-110 transition-transform"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 5l7 7-7 7M5 5l7 7-7 7" />
</svg>
</button>
{/* Settings Icon */}
<button
onClick={() => setShowSettings(true)}
className="p-2 hover:bg-accent rounded-md transition-colors duration-200"
aria-label={t('versionUpdate.ariaLabels.settings')}
title={t('versionUpdate.ariaLabels.settings')}
>
<SettingsIcon className="w-5 h-5 text-muted-foreground hover:text-foreground transition-colors" />
</button>
{/* Update Indicator */}
{updateAvailable && (
<button
onClick={() => setShowVersionModal(true)}
className="relative p-2 hover:bg-accent rounded-md transition-colors duration-200"
aria-label={t('versionUpdate.ariaLabels.updateAvailable')}
title={t('versionUpdate.ariaLabels.updateAvailable')}
>
<Sparkles className="w-5 h-5 text-blue-500" />
<span className="absolute top-1 right-1 w-2 h-2 bg-blue-500 rounded-full animate-pulse" />
</button>
)}
</div>
)}
</div>
</div> </div>
)} )}
@@ -701,14 +646,10 @@ function AppContent() {
loadingProgress={loadingProgress} loadingProgress={loadingProgress}
onRefresh={handleSidebarRefresh} onRefresh={handleSidebarRefresh}
onShowSettings={() => setShowSettings(true)} onShowSettings={() => setShowSettings(true)}
updateAvailable={updateAvailable}
latestVersion={latestVersion}
currentVersion={currentVersion}
releaseInfo={releaseInfo}
onShowVersionModal={() => setShowVersionModal(true)}
isPWA={isPWA} isPWA={isPWA}
isMobile={isMobile} isMobile={isMobile}
onToggleSidebar={() => setSidebarVisible(false)} onToggleSidebar={() => setSidebarVisible(false)}
isCollapsed={false}
/> />
</div> </div>
</div> </div>
@@ -780,15 +721,6 @@ function AppContent() {
projects={projects} projects={projects}
initialTab={settingsInitialTab} initialTab={settingsInitialTab}
/> />
{/* Version Upgrade Modal */}
<VersionUpgradeModal
isOpen={showVersionModal}
onClose={() => setShowVersionModal(false)}
releaseInfo={releaseInfo}
currentVersion={currentVersion}
latestVersion={latestVersion}
/>
</div> </div>
); );
} }

View File

@@ -13,6 +13,8 @@ import CursorLogo from './CursorLogo.jsx';
import CodexLogo from './CodexLogo.jsx'; import CodexLogo from './CodexLogo.jsx';
import TaskIndicator from './TaskIndicator'; import TaskIndicator from './TaskIndicator';
import ProjectCreationWizard from './ProjectCreationWizard'; import ProjectCreationWizard from './ProjectCreationWizard';
import VersionUpgradeModal from './modals/VersionUpgradeModal';
import { useVersionCheck } from '../hooks/useVersionCheck';
import { api } from '../utils/api'; import { api } from '../utils/api';
import { useTaskMaster } from '../contexts/TaskMasterContext'; import { useTaskMaster } from '../contexts/TaskMasterContext';
import { useTasksSettings } from '../contexts/TasksSettingsContext'; import { useTasksSettings } from '../contexts/TasksSettingsContext';
@@ -57,16 +59,14 @@ function Sidebar({
loadingProgress, loadingProgress,
onRefresh, onRefresh,
onShowSettings, onShowSettings,
updateAvailable,
latestVersion,
currentVersion,
releaseInfo,
onShowVersionModal,
isPWA, isPWA,
isMobile, isMobile,
onToggleSidebar onToggleSidebar,
isCollapsed = false,
onExpandSidebar
}) { }) {
const { t } = useTranslation('sidebar'); const { t } = useTranslation(['sidebar', 'common']);
const { updateAvailable, latestVersion, currentVersion, releaseInfo } = useVersionCheck('siteboon', 'claudecodeui');
const [expandedProjects, setExpandedProjects] = useState(new Set()); const [expandedProjects, setExpandedProjects] = useState(new Set());
const [editingProject, setEditingProject] = useState(null); const [editingProject, setEditingProject] = useState(null);
const [showNewProject, setShowNewProject] = useState(false); const [showNewProject, setShowNewProject] = useState(false);
@@ -84,6 +84,7 @@ function Sidebar({
const [deletingProjects, setDeletingProjects] = useState(new Set()); const [deletingProjects, setDeletingProjects] = useState(new Set());
const [deleteConfirmation, setDeleteConfirmation] = useState(null); // { project, sessionCount } const [deleteConfirmation, setDeleteConfirmation] = useState(null); // { project, sessionCount }
const [sessionDeleteConfirmation, setSessionDeleteConfirmation] = useState(null); // { projectName, sessionId, sessionTitle, provider } const [sessionDeleteConfirmation, setSessionDeleteConfirmation] = useState(null); // { projectName, sessionId, sessionTitle, provider }
const [showVersionModal, setShowVersionModal] = useState(false);
// TaskMaster context // TaskMaster context
const { setCurrentProject, mcpServerStatus } = useTaskMaster(); const { setCurrentProject, mcpServerStatus } = useTaskMaster();
@@ -493,8 +494,53 @@ function Sidebar({
setCurrentProject(project); setCurrentProject(project);
}; };
const collapsedSidebar = (
<div className="h-full flex flex-col items-center py-4 gap-4 bg-card">
<button
onClick={onExpandSidebar}
className="p-2 hover:bg-accent rounded-md transition-colors duration-200 group"
aria-label={t('common:versionUpdate.ariaLabels.showSidebar')}
title={t('common:versionUpdate.ariaLabels.showSidebar')}
>
<svg
className="w-5 h-5 text-foreground group-hover:scale-110 transition-transform"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 5l7 7-7 7M5 5l7 7-7 7" />
</svg>
</button>
<button
onClick={onShowSettings}
className="p-2 hover:bg-accent rounded-md transition-colors duration-200"
aria-label={t('actions.settings')}
title={t('actions.settings')}
>
<Settings className="w-5 h-5 text-muted-foreground hover:text-foreground transition-colors" />
</button>
{updateAvailable && (
<button
onClick={() => setShowVersionModal(true)}
className="relative p-2 hover:bg-accent rounded-md transition-colors duration-200"
aria-label={t('common:versionUpdate.ariaLabels.updateAvailable')}
title={t('common:versionUpdate.ariaLabels.updateAvailable')}
>
<Sparkles className="w-5 h-5 text-blue-500" />
<span className="absolute top-1 right-1 w-2 h-2 bg-blue-500 rounded-full animate-pulse" />
</button>
)}
</div>
);
return ( return (
<> <>
{isCollapsed ? (
collapsedSidebar
) : (
<>
{/* Project Creation Wizard Modal - Rendered via Portal at document root for full-screen on mobile */} {/* Project Creation Wizard Modal - Rendered via Portal at document root for full-screen on mobile */}
{showNewProject && ReactDOM.createPortal( {showNewProject && ReactDOM.createPortal(
<ProjectCreationWizard <ProjectCreationWizard
@@ -1474,7 +1520,7 @@ function Sidebar({
<Button <Button
variant="ghost" variant="ghost"
className="w-full justify-start gap-3 p-3 h-auto font-normal text-left hover:bg-blue-50 dark:hover:bg-blue-900/20 transition-colors duration-200 border border-blue-200 dark:border-blue-700 rounded-lg mb-2" className="w-full justify-start gap-3 p-3 h-auto font-normal text-left hover:bg-blue-50 dark:hover:bg-blue-900/20 transition-colors duration-200 border border-blue-200 dark:border-blue-700 rounded-lg mb-2"
onClick={onShowVersionModal} onClick={() => setShowVersionModal(true)}
> >
<div className="relative"> <div className="relative">
<svg className="w-4 h-4 text-blue-600 dark:text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-4 h-4 text-blue-600 dark:text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -1495,7 +1541,7 @@ function Sidebar({
<div className="md:hidden p-3 pb-2"> <div className="md:hidden p-3 pb-2">
<button <button
className="w-full h-12 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-700 rounded-xl flex items-center justify-start gap-3 px-4 active:scale-[0.98] transition-all duration-150" className="w-full h-12 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-700 rounded-xl flex items-center justify-start gap-3 px-4 active:scale-[0.98] transition-all duration-150"
onClick={onShowVersionModal} onClick={() => setShowVersionModal(true)}
> >
<div className="relative"> <div className="relative">
<svg className="w-5 h-5 text-blue-600 dark:text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-5 h-5 text-blue-600 dark:text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -1539,7 +1585,18 @@ function Sidebar({
<span className="text-xs">{t('actions.settings')}</span> <span className="text-xs">{t('actions.settings')}</span>
</Button> </Button>
</div> </div>
</div> </div>
</>
)}
<VersionUpgradeModal
isOpen={showVersionModal}
onClose={() => setShowVersionModal(false)}
releaseInfo={releaseInfo}
currentVersion={currentVersion}
latestVersion={latestVersion}
/>
</> </>
); );
} }