Skip to content
Open
Show file tree
Hide file tree
Changes from all 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/
37 changes: 29 additions & 8 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 @@ -70,7 +70,7 @@ import { COLOR_THEMES, UI_SCALE_MIN, UI_SCALE_MAX, UI_SCALE_DEFAULT } from '../s
import type { Task, Project, ColorTheme } from '../shared/types';
import { ProjectTabBar } from './components/ProjectTabBar';
import { AddProjectModal } from './components/AddProjectModal';
import { ViewStateProvider } from './contexts/ViewStateContext';
import { ViewStateProvider, useViewState } from './contexts/ViewStateContext';
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Use the @/* path alias instead of a relative path.

Line 73 introduces a new relative import in the renderer. Per coding guidelines, all renderer imports should use the @/* path alias.

♻️ Proposed fix
-import { ViewStateProvider, useViewState } from './contexts/ViewStateContext';
+import { ViewStateProvider, useViewState } from '@/contexts/ViewStateContext';

As per coding guidelines: "Use path aliases from tsconfig.json in frontend code: @/* for renderer".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { ViewStateProvider, useViewState } from './contexts/ViewStateContext';
import { ViewStateProvider, useViewState } from '@/contexts/ViewStateContext';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/frontend/src/renderer/App.tsx` at line 73, Replace the relative import
for the ViewState context with the project path alias: change the import of
ViewStateProvider and useViewState in App.tsx to use the "@/..." alias
(referencing the same module that currently exports ViewStateProvider and
useViewState) so imports use the renderer path alias per tsconfig instead of a
relative path.


// Version constant for version-specific warnings (e.g., reauthentication notices)
const VERSION_WARNING_275 = '2.7.5';
Expand All @@ -82,7 +82,9 @@ interface ProjectTabBarWithContextProps {
onProjectSelect: (projectId: string) => void;
onProjectClose: (projectId: string) => void;
onAddProject: () => void;
onSettingsClick: () => void;
onRefresh?: () => void;
isRefreshing?: boolean;
tasks: Task[];
}

function ProjectTabBarWithContext({
Expand All @@ -91,16 +93,30 @@ function ProjectTabBarWithContext({
onProjectSelect,
onProjectClose,
onAddProject,
onSettingsClick
onRefresh,
isRefreshing,
tasks
}: ProjectTabBarWithContextProps) {
// Get showArchived from shared context for cross-view sync with Ideation
const { showArchived, toggleShowArchived } = useViewState();

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

return (
<ProjectTabBar
projects={projects}
activeProjectId={activeProjectId}
onProjectSelect={onProjectSelect}
onProjectClose={onProjectClose}
onAddProject={onAddProject}
onSettingsClick={onSettingsClick}
onRefresh={onRefresh}
isRefreshing={isRefreshing}
showArchived={showArchived}
onToggleArchived={toggleShowArchived}
archivedCount={archivedCount}
/>
);
}
Expand Down Expand Up @@ -824,6 +840,12 @@ export function App() {
}
};

// Compute kanban-specific props once to avoid repetitive ternaries
const isKanban = activeView === 'kanban';
const kanbanOnlyProps = isKanban
? { onRefresh: handleRefreshTasks, isRefreshing: isRefreshingTasks }
: {};

return (
<ViewStateProvider>
<TooltipProvider>
Expand Down Expand Up @@ -854,7 +876,8 @@ export function App() {
onProjectSelect={handleProjectTabSelect}
onProjectClose={handleProjectTabClose}
onAddProject={handleAddProject}
onSettingsClick={() => setIsSettingsDialogOpen(true)}
tasks={tasks}
{...kanbanOnlyProps}
/>
</SortableContext>

Expand All @@ -881,8 +904,6 @@ export function App() {
tasks={tasks}
onTaskClick={handleTaskClick}
onNewTaskClick={() => setIsNewTaskDialogOpen(true)}
onRefresh={handleRefreshTasks}
isRefreshing={isRefreshingTasks}
/>
)}
{/* TerminalGrid is always mounted but hidden when not active to preserve terminal state */}
Expand Down
93 changes: 18 additions & 75 deletions apps/frontend/src/renderer/components/KanbanBoard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,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 +69,6 @@ interface KanbanBoardProps {
tasks: Task[];
onTaskClick: (task: Task) => void;
onNewTaskClick?: () => void;
onRefresh?: () => void;
isRefreshing?: boolean;
}

interface DroppableColumnProps {
Expand All @@ -84,9 +82,6 @@ interface DroppableColumnProps {
onQueueSettings?: () => void;
onQueueAll?: () => void;
maxParallelTasks?: number;
archivedCount?: number;
showArchived?: boolean;
onToggleArchived?: () => void;
// Selection props for human_review column
selectedTaskIds?: Set<string>;
onSelectAll?: () => void;
Expand Down Expand Up @@ -146,9 +141,6 @@ function droppableColumnPropsAreEqual(
if (prevProps.onQueueSettings !== nextProps.onQueueSettings) return false;
if (prevProps.onQueueAll !== nextProps.onQueueAll) return false;
if (prevProps.maxParallelTasks !== nextProps.maxParallelTasks) return false;
if (prevProps.archivedCount !== nextProps.archivedCount) return false;
if (prevProps.showArchived !== nextProps.showArchived) return false;
if (prevProps.onToggleArchived !== nextProps.onToggleArchived) return false;
if (prevProps.onSelectAll !== nextProps.onSelectAll) return false;
if (prevProps.onDeselectAll !== nextProps.onDeselectAll) return false;
if (prevProps.onToggleSelect !== nextProps.onToggleSelect) return false;
Expand Down Expand Up @@ -230,11 +222,13 @@ const getEmptyStateContent = (status: TaskStatus, t: (key: string) => string): {
}
};

const DroppableColumn = memo(function DroppableColumn({ status, tasks, onTaskClick, onStatusChange, isOver, onAddClick, onArchiveAll, onQueueSettings, onQueueAll, maxParallelTasks, archivedCount, showArchived, onToggleArchived, selectedTaskIds, onSelectAll, onDeselectAll, onToggleSelect, isCollapsed, onToggleCollapsed, columnWidth, isResizing, onResizeStart, onResizeEnd, isLocked, onToggleLocked }: DroppableColumnProps) {
const DroppableColumn = memo(function DroppableColumn({ status, tasks, onTaskClick, onStatusChange, isOver, onAddClick, onArchiveAll, onQueueSettings, onQueueAll, maxParallelTasks, selectedTaskIds, onSelectAll, onDeselectAll, onToggleSelect, isCollapsed, onToggleCollapsed, columnWidth, isResizing, onResizeStart, onResizeEnd, isLocked, onToggleLocked }: DroppableColumnProps) {
const { t } = useTranslation(['tasks', 'common']);
const { setNodeRef } = useDroppable({
id: status
});
// Get showArchived from shared context to conditionally show Archive All button
const { showArchived } = useViewState();

// Calculate selection state for this column
const taskCount = tasks.length;
Expand Down Expand Up @@ -524,33 +518,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 +601,13 @@ const DroppableColumn = memo(function DroppableColumn({ status, tasks, onTaskCli
);
}, droppableColumnPropsAreEqual);

export function KanbanBoard({ tasks, onTaskClick, onNewTaskClick, onRefresh, isRefreshing }: KanbanBoardProps) {
export function KanbanBoard({ tasks, onTaskClick, onNewTaskClick }: KanbanBoardProps) {
const { t } = useTranslation(['tasks', 'dialogs', 'common']);
const { toast } = useToast();
// Get showArchived from shared context for cross-view sync with Ideation
const { showArchived } = useViewState();
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 +668,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 +1390,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 +1434,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