Skip to content

Commit ca82ebe

Browse files
agnikagnik
authored andcommitted
feat: Bulk AI Tagging & Progress Tracking for Folder Management (#725)
- Add progress summary showing overall AI tagging status - Add bulk action buttons (AI Tag All, Tag Selected, Disable All) - Add smart sorting with collapsible sections (Completed/In Progress/Pending) - Add multi-select functionality with checkboxes - Add Checkbox UI component - Add utility functions for folder grouping and stats - Add unit tests for folder utilities Closes #725
1 parent d07d817 commit ca82ebe

File tree

12 files changed

+1078
-91
lines changed

12 files changed

+1078
-91
lines changed

frontend/package-lock.json

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"dependencies": {
2626
"@radix-ui/react-aspect-ratio": "^1.1.7",
2727
"@radix-ui/react-avatar": "^1.1.10",
28+
"@radix-ui/react-checkbox": "^1.3.3",
2829
"@radix-ui/react-dialog": "^1.1.15",
2930
"@radix-ui/react-dropdown-menu": "^2.1.15",
3031
"@radix-ui/react-label": "^2.1.7",
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import React from 'react';
2+
import { Sparkles, SparklesIcon } from 'lucide-react';
3+
import { Button } from '@/components/ui/button';
4+
import { Checkbox } from '@/components/ui/checkbox';
5+
6+
interface FolderBulkActionsProps {
7+
totalCount: number;
8+
selectedCount: number;
9+
pendingCount: number;
10+
enabledCount: number;
11+
isAllSelected: boolean;
12+
onSelectAll: () => void;
13+
onDeselectAll: () => void;
14+
onEnableAll: () => void;
15+
onEnableSelected: () => void;
16+
onDisableAll: () => void;
17+
onDisableSelected: () => void;
18+
isEnabling: boolean;
19+
isDisabling: boolean;
20+
}
21+
22+
/**
23+
* Bulk action buttons for folder management
24+
*/
25+
export const FolderBulkActions: React.FC<FolderBulkActionsProps> = ({
26+
totalCount,
27+
selectedCount,
28+
pendingCount,
29+
enabledCount,
30+
isAllSelected,
31+
onSelectAll,
32+
onDeselectAll,
33+
onEnableAll,
34+
onEnableSelected,
35+
onDisableAll,
36+
onDisableSelected,
37+
isEnabling,
38+
isDisabling,
39+
}) => {
40+
if (totalCount === 0) return null;
41+
42+
const isLoading = isEnabling || isDisabling;
43+
44+
return (
45+
<div className="border-border mb-4 flex flex-wrap items-center justify-between gap-3 rounded-lg border p-3">
46+
{/* Selection Controls */}
47+
<div className="flex items-center gap-3">
48+
<div
49+
className="flex cursor-pointer items-center gap-2"
50+
onClick={isAllSelected ? onDeselectAll : onSelectAll}
51+
>
52+
<Checkbox
53+
checked={isAllSelected}
54+
onCheckedChange={isAllSelected ? onDeselectAll : onSelectAll}
55+
className="cursor-pointer"
56+
/>
57+
<span className="text-sm">
58+
{isAllSelected ? 'Deselect All' : 'Select All'} ({totalCount})
59+
</span>
60+
</div>
61+
62+
{selectedCount > 0 && (
63+
<span className="text-muted-foreground text-sm">
64+
{selectedCount} selected
65+
</span>
66+
)}
67+
</div>
68+
69+
{/* Action Buttons */}
70+
<div className="flex flex-wrap gap-2">
71+
{/* Enable Actions */}
72+
{pendingCount > 0 && (
73+
<Button
74+
variant="outline"
75+
size="sm"
76+
onClick={onEnableAll}
77+
disabled={isLoading}
78+
className="gap-1.5"
79+
>
80+
<Sparkles className="h-3.5 w-3.5" />
81+
AI Tag All ({pendingCount})
82+
</Button>
83+
)}
84+
85+
{selectedCount > 0 && (
86+
<Button
87+
variant="default"
88+
size="sm"
89+
onClick={onEnableSelected}
90+
disabled={isLoading}
91+
className="gap-1.5"
92+
>
93+
<SparklesIcon className="h-3.5 w-3.5" />
94+
Tag Selected ({selectedCount})
95+
</Button>
96+
)}
97+
98+
{/* Disable Actions */}
99+
{enabledCount > 0 && (
100+
<Button
101+
variant="ghost"
102+
size="sm"
103+
onClick={onDisableAll}
104+
disabled={isLoading}
105+
className="text-muted-foreground hover:text-destructive gap-1.5"
106+
>
107+
Disable All ({enabledCount})
108+
</Button>
109+
)}
110+
111+
{selectedCount > 0 && (
112+
<Button
113+
variant="ghost"
114+
size="sm"
115+
onClick={onDisableSelected}
116+
disabled={isLoading}
117+
className="text-muted-foreground hover:text-destructive gap-1.5"
118+
>
119+
Disable Selected
120+
</Button>
121+
)}
122+
</div>
123+
</div>
124+
);
125+
};
126+
127+
export default FolderBulkActions;
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import React from 'react';
2+
import { CheckCircle2, Loader2, Clock } from 'lucide-react';
3+
import { Progress } from '@/components/ui/progress';
4+
import { FolderStats } from '@/utils/folderUtils';
5+
6+
interface FolderProgressSummaryProps {
7+
stats: FolderStats;
8+
}
9+
10+
/**
11+
* Displays overall AI tagging progress summary
12+
*/
13+
export const FolderProgressSummary: React.FC<FolderProgressSummaryProps> = ({
14+
stats,
15+
}) => {
16+
const { total, completed, inProgress, pending, overallPercentage } = stats;
17+
18+
if (total === 0) return null;
19+
20+
const foldersWithTagging = completed + inProgress;
21+
22+
return (
23+
<div className="bg-muted/50 mb-4 rounded-lg p-4">
24+
<div className="mb-3 flex items-center justify-between">
25+
<h3 className="text-sm font-medium">AI Tagging Progress</h3>
26+
<span className="text-muted-foreground text-sm">
27+
{foldersWithTagging}/{total} folders enabled
28+
</span>
29+
</div>
30+
31+
<Progress
32+
value={overallPercentage}
33+
className="mb-3 h-2"
34+
indicatorClassName={
35+
overallPercentage >= 100 ? 'bg-green-500' : 'bg-blue-500'
36+
}
37+
/>
38+
39+
<div className="flex flex-wrap gap-4 text-sm">
40+
<div className="flex items-center gap-1.5">
41+
<CheckCircle2 className="h-4 w-4 text-green-500" />
42+
<span className="text-muted-foreground">Completed:</span>
43+
<span className="font-medium">{completed}</span>
44+
</div>
45+
46+
<div className="flex items-center gap-1.5">
47+
<Loader2 className="h-4 w-4 text-blue-500" />
48+
<span className="text-muted-foreground">In Progress:</span>
49+
<span className="font-medium">{inProgress}</span>
50+
</div>
51+
52+
<div className="flex items-center gap-1.5">
53+
<Clock className="h-4 w-4 text-gray-400" />
54+
<span className="text-muted-foreground">Pending:</span>
55+
<span className="font-medium">{pending}</span>
56+
</div>
57+
</div>
58+
</div>
59+
);
60+
};
61+
62+
export default FolderProgressSummary;

0 commit comments

Comments
 (0)