mirror of
https://github.com/siteboon/claudecodeui.git
synced 2025-12-09 00:39:39 +00:00
feat: Add project sorting by date option
- Added ability to sort projects by name or most recent session activity - Added projectSortOrder state and localStorage persistence - Added UI controls in ToolsSettings to switch between sort modes - Projects with no sessions sort last when sorting by date - Real-time updates when sort preference changes
This commit is contained in:
@@ -61,6 +61,7 @@ function Sidebar({
|
||||
const [additionalSessions, setAdditionalSessions] = useState({});
|
||||
const [initialSessionsLoaded, setInitialSessionsLoaded] = useState(new Set());
|
||||
const [currentTime, setCurrentTime] = useState(new Date());
|
||||
const [projectSortOrder, setProjectSortOrder] = useState('name');
|
||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||
const [editingSession, setEditingSession] = useState(null);
|
||||
const [editingSessionName, setEditingSessionName] = useState('');
|
||||
@@ -114,6 +115,45 @@ function Sidebar({
|
||||
}
|
||||
}, [projects, isLoading]);
|
||||
|
||||
// Load project sort order from settings
|
||||
useEffect(() => {
|
||||
const loadSortOrder = () => {
|
||||
try {
|
||||
const savedSettings = localStorage.getItem('claude-tools-settings');
|
||||
if (savedSettings) {
|
||||
const settings = JSON.parse(savedSettings);
|
||||
setProjectSortOrder(settings.projectSortOrder || 'name');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading sort order:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Load initially
|
||||
loadSortOrder();
|
||||
|
||||
// Listen for storage changes
|
||||
const handleStorageChange = (e) => {
|
||||
if (e.key === 'claude-tools-settings') {
|
||||
loadSortOrder();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('storage', handleStorageChange);
|
||||
|
||||
// Also check periodically when component is focused (for same-tab changes)
|
||||
const checkInterval = setInterval(() => {
|
||||
if (document.hasFocus()) {
|
||||
loadSortOrder();
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('storage', handleStorageChange);
|
||||
clearInterval(checkInterval);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const toggleProject = (projectName) => {
|
||||
const newExpanded = new Set(expandedProjects);
|
||||
if (newExpanded.has(projectName)) {
|
||||
@@ -291,6 +331,33 @@ function Sidebar({
|
||||
return [...initialSessions, ...additional];
|
||||
};
|
||||
|
||||
// Helper function to get the last activity date for a project
|
||||
const getProjectLastActivity = (project) => {
|
||||
const allSessions = getAllSessions(project);
|
||||
if (allSessions.length === 0) {
|
||||
return new Date(0); // Return epoch date for projects with no sessions
|
||||
}
|
||||
|
||||
// Find the most recent session activity
|
||||
const mostRecentDate = allSessions.reduce((latest, session) => {
|
||||
const sessionDate = new Date(session.lastActivity);
|
||||
return sessionDate > latest ? sessionDate : latest;
|
||||
}, new Date(0));
|
||||
|
||||
return mostRecentDate;
|
||||
};
|
||||
|
||||
// Sort projects based on selected order
|
||||
const sortedProjects = [...projects].sort((a, b) => {
|
||||
if (projectSortOrder === 'date') {
|
||||
// Sort by most recent activity (descending)
|
||||
return getProjectLastActivity(b) - getProjectLastActivity(a);
|
||||
} else {
|
||||
// Sort by name (ascending)
|
||||
return a.name.localeCompare(b.name);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col bg-card md:select-none">
|
||||
{/* Header */}
|
||||
@@ -499,7 +566,7 @@ function Sidebar({
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
projects.map((project) => {
|
||||
sortedProjects.map((project) => {
|
||||
const isExpanded = expandedProjects.has(project.name);
|
||||
const isSelected = selectedProject?.name === project.name;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Button } from './ui/button';
|
||||
import { Input } from './ui/input';
|
||||
import { ScrollArea } from './ui/scroll-area';
|
||||
import { Badge } from './ui/badge';
|
||||
import { X, Plus, Settings, Shield, AlertTriangle, Moon, Sun } from 'lucide-react';
|
||||
import { X, Plus, Settings, Shield, AlertTriangle, Moon, Sun, FolderOpen, SortAsc } from 'lucide-react';
|
||||
import { useTheme } from '../contexts/ThemeContext';
|
||||
|
||||
function ToolsSettings({ isOpen, onClose }) {
|
||||
@@ -15,6 +15,7 @@ function ToolsSettings({ isOpen, onClose }) {
|
||||
const [skipPermissions, setSkipPermissions] = useState(false);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [saveStatus, setSaveStatus] = useState(null);
|
||||
const [projectSortOrder, setProjectSortOrder] = useState('name');
|
||||
|
||||
// Common tool patterns
|
||||
const commonTools = [
|
||||
@@ -51,11 +52,13 @@ function ToolsSettings({ isOpen, onClose }) {
|
||||
setAllowedTools(settings.allowedTools || []);
|
||||
setDisallowedTools(settings.disallowedTools || []);
|
||||
setSkipPermissions(settings.skipPermissions || false);
|
||||
setProjectSortOrder(settings.projectSortOrder || 'name');
|
||||
} else {
|
||||
// Set defaults
|
||||
setAllowedTools([]);
|
||||
setDisallowedTools([]);
|
||||
setSkipPermissions(false);
|
||||
setProjectSortOrder('name');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading tool settings:', error);
|
||||
@@ -63,6 +66,7 @@ function ToolsSettings({ isOpen, onClose }) {
|
||||
setAllowedTools([]);
|
||||
setDisallowedTools([]);
|
||||
setSkipPermissions(false);
|
||||
setProjectSortOrder('name');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -75,6 +79,7 @@ function ToolsSettings({ isOpen, onClose }) {
|
||||
allowedTools,
|
||||
disallowedTools,
|
||||
skipPermissions,
|
||||
projectSortOrder,
|
||||
lastUpdated: new Date().toISOString()
|
||||
};
|
||||
|
||||
@@ -212,6 +217,56 @@ function ToolsSettings({ isOpen, onClose }) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Project Sorting */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<FolderOpen className="w-5 h-5 text-purple-500" />
|
||||
<h3 className="text-lg font-medium text-foreground">
|
||||
Project Sorting
|
||||
</h3>
|
||||
</div>
|
||||
<div className="bg-purple-50 dark:bg-purple-900/20 border border-purple-200 dark:border-purple-800 rounded-lg p-4">
|
||||
<div className="space-y-3">
|
||||
<div className="text-sm text-muted-foreground mb-3">
|
||||
Choose how projects are sorted in the sidebar
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => setProjectSortOrder('name')}
|
||||
className={`flex-1 px-4 py-2 rounded-lg border transition-colors ${
|
||||
projectSortOrder === 'name'
|
||||
? 'bg-purple-600 text-white border-purple-600'
|
||||
: 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<SortAsc className="w-4 h-4" />
|
||||
<span>Sort by Name</span>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setProjectSortOrder('date')}
|
||||
className={`flex-1 px-4 py-2 rounded-lg border transition-colors ${
|
||||
projectSortOrder === 'date'
|
||||
? 'bg-purple-600 text-white border-purple-600'
|
||||
: 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<SortAsc className="w-4 h-4 rotate-180" />
|
||||
<span>Sort by Date</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-xs text-purple-700 dark:text-purple-300 mt-2">
|
||||
{projectSortOrder === 'name'
|
||||
? 'Projects are sorted alphabetically by name'
|
||||
: 'Projects are sorted by most recent session activity'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Allowed Tools */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-3">
|
||||
|
||||
Reference in New Issue
Block a user