-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat: Tidying up kanban board #1900
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 15 commits
836f75f
66fc27d
7039817
aa7b13c
3f4110e
8e57a8e
a0dd8a7
00ecce1
a4f4d6c
39d9033
b13b003
2139966
ac14865
3e78312
f2a2b4f
d0a5bbe
1f63c2e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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/ |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -83,6 +83,11 @@ | |||||||||||||
| onProjectClose: (projectId: string) => void; | ||||||||||||||
| onAddProject: () => void; | ||||||||||||||
| onSettingsClick: () => void; | ||||||||||||||
| onRefresh?: () => void; | ||||||||||||||
| isRefreshing?: boolean; | ||||||||||||||
| showArchived?: boolean; | ||||||||||||||
| onToggleArchived?: () => void; | ||||||||||||||
| archivedCount?: number; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| function ProjectTabBarWithContext({ | ||||||||||||||
|
|
@@ -91,7 +96,12 @@ | |||||||||||||
| onProjectSelect, | ||||||||||||||
| onProjectClose, | ||||||||||||||
| onAddProject, | ||||||||||||||
| onSettingsClick | ||||||||||||||
| onSettingsClick, | ||||||||||||||
| onRefresh, | ||||||||||||||
| isRefreshing, | ||||||||||||||
| showArchived, | ||||||||||||||
| onToggleArchived, | ||||||||||||||
| archivedCount | ||||||||||||||
| }: ProjectTabBarWithContextProps) { | ||||||||||||||
| return ( | ||||||||||||||
| <ProjectTabBar | ||||||||||||||
|
|
@@ -101,6 +111,11 @@ | |||||||||||||
| onProjectClose={onProjectClose} | ||||||||||||||
| onAddProject={onAddProject} | ||||||||||||||
| onSettingsClick={onSettingsClick} | ||||||||||||||
| onRefresh={onRefresh} | ||||||||||||||
| isRefreshing={isRefreshing} | ||||||||||||||
| showArchived={showArchived} | ||||||||||||||
| onToggleArchived={onToggleArchived} | ||||||||||||||
| archivedCount={archivedCount} | ||||||||||||||
| /> | ||||||||||||||
| ); | ||||||||||||||
| } | ||||||||||||||
|
|
@@ -126,6 +141,7 @@ | |||||||||||||
| const setActiveProject = useProjectStore((state) => state.setActiveProject); | ||||||||||||||
| const reorderTabs = useProjectStore((state) => state.reorderTabs); | ||||||||||||||
| const tasks = useTaskStore((state) => state.tasks); | ||||||||||||||
| const isLoadingTasks = useTaskStore((state) => state.isLoading); | ||||||||||||||
|
||||||||||||||
| const settings = useSettingsStore((state) => state.settings); | ||||||||||||||
| const settingsLoading = useSettingsStore((state) => state.isLoading); | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -143,6 +159,7 @@ | |||||||||||||
| 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); | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -824,6 +841,16 @@ | |||||||||||||
| } | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| // Handle toggle archived visibility | ||||||||||||||
| const handleToggleArchived = () => { | ||||||||||||||
| setShowArchived((prev) => !prev); | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| // Calculate archived task count | ||||||||||||||
| const archivedCount = tasks.filter( | ||||||||||||||
| (task) => task.metadata?.archivedAt | ||||||||||||||
| ).length; | ||||||||||||||
|
||||||||||||||
| const archivedCount = tasks.filter( | |
| (task) => task.metadata?.archivedAt | |
| ).length; | |
| const archivedCount = useMemo(() => tasks.filter( | |
| (task) => task.metadata?.archivedAt | |
| ).length, [tasks]); |
| 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, | ||
|
|
@@ -19,7 +18,7 @@ | |
| 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, AlertCircle, RefreshCw, 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'; | ||
|
|
@@ -69,8 +68,7 @@ | |
| tasks: Task[]; | ||
| onTaskClick: (task: Task) => void; | ||
| onNewTaskClick?: () => void; | ||
| onRefresh?: () => void; | ||
| isRefreshing?: boolean; | ||
| showArchived?: boolean; | ||
| } | ||
|
|
||
| interface DroppableColumnProps { | ||
|
|
@@ -513,7 +511,7 @@ | |
| <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" | ||
|
|
@@ -524,33 +522,6 @@ | |
| <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> | ||
|
|
||
|
|
@@ -634,12 +605,11 @@ | |
| ); | ||
| }, 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); | ||
|
|
@@ -700,12 +670,6 @@ | |
| 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; | ||
|
|
@@ -1428,36 +1392,20 @@ | |
|
|
||
| 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor UX gap: "Expand All" is hidden when fewer than 3 columns are collapsed. With 1 or 2 columns collapsed, 💡 Suggested threshold adjustment- {collapsedColumnCount >= 3 && (
+ {collapsedColumnCount >= 1 && (🤖 Prompt for AI Agents |
||
|
|
@@ -1488,9 +1436,6 @@ | |
| } : 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} | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The constant
isLoadingTasksis declared but its value is never read. This unused variable should be removed to improve code clarity and maintainability.