Skip to content
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
836f75f
auto-claude: subtask-1-1 - Update KanbanBoard component: remove refre…
fireapache Jan 13, 2026
66fc27d
auto-claude: subtask-1-2 - Update ProjectTabBar component: add Refres…
fireapache Jan 13, 2026
7039817
auto-claude: subtask-1-3 - Update SortableProjectTab component: remov…
fireapache Jan 13, 2026
aa7b13c
auto-claude: subtask-1-4 - Add translation keys for new tooltips in E…
fireapache Jan 13, 2026
3f4110e
auto-claude: subtask-1-5 - Update parent component to pass new props …
fireapache Jan 13, 2026
8e57a8e
fix: correct Show Archived button and Add tooltip per QA feedback
fireapache Jan 13, 2026
a0dd8a7
fix: Address QA issues (qa-requested)
fireapache Jan 13, 2026
00ecce1
fix: anchor Refresh and Show Archived buttons to right side of tab bar
fireapache Jan 14, 2026
a4f4d6c
fix: add aria-label to Show Archived button for accessibility
fireapache Jan 14, 2026
39d9033
fix: show Refresh and Show Archived buttons only on Kanban view
fireapache Jan 15, 2026
b13b003
fix: add explicit common: namespace prefix to translation keys
fireapache Jan 15, 2026
2139966
docs: clarify Kanban controls comment in ProjectTabBar
fireapache Jan 15, 2026
ac14865
refactor: simplify Show Archived button conditional rendering
fireapache Jan 15, 2026
3e78312
Merge branch 'origin/develop' into auto-claude/031-tidying-up-kanban-…
fireapache Feb 25, 2026
f2a2b4f
fix: add missing Tooltip imports to KanbanBoard and .secretsignore
fireapache Feb 25, 2026
d0a5bbe
fix: Address PR review feedback (qa-requested)
fireapache Feb 25, 2026
1f63c2e
fix: Address PR review feedback (qa-requested)
fireapache Feb 25, 2026
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
9 changes: 9 additions & 0 deletions .secretsignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Test files with mock credentials
*.test.ts
*.test.tsx
test_*.py
tests/

# Scripts with checksums/hashes (not secrets)
apps/frontend/scripts/download-python.cjs
.github/actions/
41 changes: 34 additions & 7 deletions apps/frontend/src/renderer/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useEffect } from 'react';
import { useState, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { Download, RefreshCw, AlertCircle } from 'lucide-react';
import { debugLog } from '../shared/utils/debug-logger';
Expand Down Expand Up @@ -82,7 +82,11 @@ interface ProjectTabBarWithContextProps {
onProjectSelect: (projectId: string) => void;
onProjectClose: (projectId: string) => void;
onAddProject: () => void;
onSettingsClick: () => void;
onRefresh?: () => void;
isRefreshing?: boolean;
showArchived?: boolean;
onToggleArchived?: () => void;
archivedCount?: number;
}

function ProjectTabBarWithContext({
Expand All @@ -91,7 +95,11 @@ function ProjectTabBarWithContext({
onProjectSelect,
onProjectClose,
onAddProject,
onSettingsClick
onRefresh,
isRefreshing,
showArchived,
onToggleArchived,
archivedCount
}: ProjectTabBarWithContextProps) {
return (
<ProjectTabBar
Expand All @@ -100,7 +108,11 @@ function ProjectTabBarWithContext({
onProjectSelect={onProjectSelect}
onProjectClose={onProjectClose}
onAddProject={onAddProject}
onSettingsClick={onSettingsClick}
onRefresh={onRefresh}
isRefreshing={isRefreshing}
showArchived={showArchived}
onToggleArchived={onToggleArchived}
archivedCount={archivedCount}
/>
);
}
Expand Down Expand Up @@ -143,6 +155,7 @@ export function App() {
const [settingsInitialProjectSection, setSettingsInitialProjectSection] = useState<ProjectSettingsSection | undefined>(undefined);
const [activeView, setActiveView] = useState<SidebarView>('kanban');
const [isOnboardingWizardOpen, setIsOnboardingWizardOpen] = useState(false);
const [showArchived, setShowArchived] = useState(false);
const [isVersionWarningModalOpen, setIsVersionWarningModalOpen] = useState(false);
const [isRefreshingTasks, setIsRefreshingTasks] = useState(false);

Expand Down Expand Up @@ -824,6 +837,16 @@ export function App() {
}
};

// Handle toggle archived visibility
const handleToggleArchived = () => {
setShowArchived((prev) => !prev);
};

// Calculate archived task count
const archivedCount = useMemo(() => tasks.filter(
(task) => task.metadata?.archivedAt
).length, [tasks]);

return (
<ViewStateProvider>
<TooltipProvider>
Expand Down Expand Up @@ -854,7 +877,12 @@ export function App() {
onProjectSelect={handleProjectTabSelect}
onProjectClose={handleProjectTabClose}
onAddProject={handleAddProject}
onSettingsClick={() => setIsSettingsDialogOpen(true)}
// Only show refresh/archived controls on kanban view
onRefresh={activeView === 'kanban' ? handleRefreshTasks : undefined}
isRefreshing={activeView === 'kanban' ? isRefreshingTasks : undefined}
showArchived={activeView === 'kanban' ? showArchived : undefined}
onToggleArchived={activeView === 'kanban' ? handleToggleArchived : undefined}
archivedCount={activeView === 'kanban' ? archivedCount : undefined}
/>
</SortableContext>

Expand All @@ -881,8 +909,7 @@ export function App() {
tasks={tasks}
onTaskClick={handleTaskClick}
onNewTaskClick={() => setIsNewTaskDialogOpen(true)}
onRefresh={handleRefreshTasks}
isRefreshing={isRefreshingTasks}
showArchived={showArchived}
/>
)}
{/* TerminalGrid is always mounted but hidden when not active to preserve terminal state */}
Expand Down
85 changes: 15 additions & 70 deletions apps/frontend/src/renderer/components/KanbanBoard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useState, useMemo, useEffect, useCallback, useRef, memo } from 'react';
import { useTranslation } from 'react-i18next';
import { useViewState } from '../contexts/ViewStateContext';
import {
DndContext,
DragOverlay,
Expand All @@ -19,7 +18,7 @@ import {
sortableKeyboardCoordinates,
verticalListSortingStrategy
} from '@dnd-kit/sortable';
import { Plus, Inbox, Loader2, Eye, CheckCircle2, Archive, RefreshCw, GitPullRequest, X, Settings, ListPlus, ChevronLeft, ChevronRight, ChevronsRight, Lock, Unlock, Trash2 } from 'lucide-react';
import { Plus, Inbox, Loader2, Eye, CheckCircle2, Archive, GitPullRequest, X, Settings, ListPlus, ChevronLeft, ChevronRight, ChevronsRight, Lock, Unlock, Trash2 } from 'lucide-react';
import { Checkbox } from './ui/checkbox';
import { ScrollArea } from './ui/scroll-area';
import { Button } from './ui/button';
Expand Down Expand Up @@ -69,8 +68,7 @@ interface KanbanBoardProps {
tasks: Task[];
onTaskClick: (task: Task) => void;
onNewTaskClick?: () => void;
onRefresh?: () => void;
isRefreshing?: boolean;
showArchived?: boolean;
}

interface DroppableColumnProps {
Expand Down Expand Up @@ -513,7 +511,7 @@ const DroppableColumn = memo(function DroppableColumn({ status, tasks, onTaskCli
<Settings className="h-4 w-4" />
</Button>
)}
{status === 'done' && onArchiveAll && tasks.length > 0 && !showArchived && (
{status === 'done' && onArchiveAll && tasks.length > 0 && (
<Button
variant="ghost"
size="icon"
Expand All @@ -524,33 +522,6 @@ const DroppableColumn = memo(function DroppableColumn({ status, tasks, onTaskCli
<Archive className="h-4 w-4" />
</Button>
)}
{status === 'done' && archivedCount !== undefined && archivedCount > 0 && onToggleArchived && (
<Tooltip delayDuration={200}>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
className={cn(
'h-7 w-7 transition-colors relative',
showArchived
? 'text-primary bg-primary/10 hover:bg-primary/20'
: 'hover:bg-muted-foreground/10 hover:text-muted-foreground'
)}
onClick={onToggleArchived}
aria-pressed={showArchived}
aria-label={t('common:accessibility.toggleShowArchivedAriaLabel')}
>
<Archive className="h-4 w-4" />
<span className="absolute -top-1 -right-1 text-[10px] font-medium bg-muted rounded-full min-w-[14px] h-[14px] flex items-center justify-center">
{archivedCount}
</span>
</Button>
</TooltipTrigger>
<TooltipContent>
{showArchived ? t('common:projectTab.hideArchived') : t('common:projectTab.showArchived')}
</TooltipContent>
</Tooltip>
)}
</div>
</div>

Expand Down Expand Up @@ -634,12 +605,11 @@ const DroppableColumn = memo(function DroppableColumn({ status, tasks, onTaskCli
);
}, droppableColumnPropsAreEqual);

export function KanbanBoard({ tasks, onTaskClick, onNewTaskClick, onRefresh, isRefreshing }: KanbanBoardProps) {
export function KanbanBoard({ tasks, onTaskClick, onNewTaskClick, showArchived = false }: KanbanBoardProps) {
const { t } = useTranslation(['tasks', 'dialogs', 'common']);
const { toast } = useToast();
const [activeTask, setActiveTask] = useState<Task | null>(null);
const [overColumnId, setOverColumnId] = useState<string | null>(null);
const { showArchived, toggleShowArchived } = useViewState();

// Project store for queue settings
const projects = useProjectStore((state) => state.projects);
Expand Down Expand Up @@ -700,12 +670,6 @@ export function KanbanBoard({ tasks, onTaskClick, onNewTaskClick, onRefresh, isR
error: undefined
});

// Calculate archived count for Done column button
const archivedCount = useMemo(() =>
tasks.filter(t => t.metadata?.archivedAt).length,
[tasks]
);

// Calculate collapsed column count for "Expand All" button
const collapsedColumnCount = useMemo(() => {
if (!columnPreferences) return 0;
Expand Down Expand Up @@ -1428,36 +1392,20 @@ export function KanbanBoard({ tasks, onTaskClick, onNewTaskClick, onRefresh, isR

return (
<div className="flex h-full flex-col">
{/* Kanban header with refresh button and expand all */}
{(onRefresh || collapsedColumnCount >= 3) && (
{/* Kanban header with expand all button */}
{collapsedColumnCount >= 3 && (
<div className="flex items-center justify-between px-6 pt-4 pb-2">
<div className="flex items-center gap-2">
{/* Expand All button - appears when 3+ columns are collapsed */}
{collapsedColumnCount >= 3 && (
<Button
variant="outline"
size="sm"
onClick={handleExpandAll}
className="gap-2 text-muted-foreground hover:text-foreground"
>
<ChevronsRight className="h-4 w-4" />
{t('tasks:kanban.expandAll')}
</Button>
)}
</div>
<div className="flex items-center gap-2">
{onRefresh && (
<Button
variant="ghost"
size="sm"
onClick={onRefresh}
disabled={isRefreshing}
className="gap-2 text-muted-foreground hover:text-foreground"
>
<RefreshCw className={cn("h-4 w-4", isRefreshing && "animate-spin")} />
{isRefreshing ? t('common:buttons.refreshing') : t('tasks:refreshTasks')}
</Button>
)}
<Button
variant="outline"
size="sm"
onClick={handleExpandAll}
className="gap-2 text-muted-foreground hover:text-foreground"
>
<ChevronsRight className="h-4 w-4" />
{t('tasks:kanban.expandAll')}
</Button>
</div>
</div>
)}
Comment on lines +1393 to 1409
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor UX gap: "Expand All" is hidden when fewer than 3 columns are collapsed.

With 1 or 2 columns collapsed, collapsedColumnCount >= 3 evaluates to false and the entire header (including the only available "Expand All" shortcut) is hidden. Users must expand each collapsed column individually via the per-column button. Consider lowering the threshold to >= 1 or >= 2 to surface the convenience button earlier.

💡 Suggested threshold adjustment
-      {collapsedColumnCount >= 3 && (
+      {collapsedColumnCount >= 1 && (
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/frontend/src/renderer/components/KanbanBoard.tsx` around lines 1393 -
1409, The Expand All button is only rendered when collapsedColumnCount >= 3 so
it’s hidden when 1–2 columns are collapsed; change the conditional in the
KanbanBoard JSX to use a lower threshold (e.g., collapsedColumnCount >= 1 or >=
2) so the Button (onClick={handleExpandAll}, label t('tasks:kanban.expandAll'))
is shown earlier, and update the surrounding comment string that currently says
"appears when 3+ columns are collapsed" to reflect the new threshold.

Expand Down Expand Up @@ -1488,9 +1436,6 @@ export function KanbanBoard({ tasks, onTaskClick, onNewTaskClick, onRefresh, isR
} : undefined}
onArchiveAll={status === 'done' ? handleArchiveAll : undefined}
maxParallelTasks={status === 'in_progress' ? maxParallelTasks : undefined}
archivedCount={status === 'done' ? archivedCount : undefined}
showArchived={status === 'done' ? showArchived : undefined}
onToggleArchived={status === 'done' ? toggleShowArchived : undefined}
selectedTaskIds={selectedTaskIds}
onSelectAll={() => selectAllTasks(status)}
onDeselectAll={deselectAllTasks}
Expand Down
Loading
Loading