Skip to content

Commit 24afce4

Browse files
feat: Multi-select script download with progress tracking (#121)
* feat: Add multi-select script download with progress tracking - Add checkbox selection to script cards (both card and list views) - Implement individual script downloads with real-time progress - Add progress bar with visual indicators (✓ success, ✗ failed, ⟳ in-progress) - Add batch download buttons (Download Selected, Download All Filtered) - Add user-friendly error messages with specific guidance - Add persistent progress bar with manual dismiss option - Clear selection when switching between card/list views - Update card download status immediately after completion Features: - Multi-select with checkboxes on script cards - Real-time progress tracking during downloads - Detailed error reporting with actionable messages - Visual progress indicators for each script - Batch download functionality for selected or filtered scripts - Persistent progress bar until manually dismissed - Automatic card status updates after download completion * fix: Resolve ESLint errors - Replace logical OR operators (||) with nullish coalescing (??) for safer null/undefined handling - Replace for loop with for-of loop for better iteration - Remove unused variable 'results' - Fix all TypeScript ESLint warnings and errors * fix: Resolve TypeScript error in loadMultipleScripts - Use type guard to check for 'error' property before accessing - Fix 'Property error does not exist' TypeScript error - Ensure safe access to error property in result object
1 parent 9d83697 commit 24afce4

File tree

6 files changed

+446
-72
lines changed

6 files changed

+446
-72
lines changed

scripts/ct/debian.sh

Lines changed: 0 additions & 43 deletions
This file was deleted.

scripts/install/debian-install.sh

Lines changed: 0 additions & 24 deletions
This file was deleted.

src/app/_components/ScriptCard.tsx

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,49 @@ import { TypeBadge, UpdateableBadge } from './Badge';
88
interface ScriptCardProps {
99
script: ScriptCard;
1010
onClick: (script: ScriptCard) => void;
11+
isSelected?: boolean;
12+
onToggleSelect?: (slug: string) => void;
1113
}
1214

13-
export function ScriptCard({ script, onClick }: ScriptCardProps) {
15+
export function ScriptCard({ script, onClick, isSelected = false, onToggleSelect }: ScriptCardProps) {
1416
const [imageError, setImageError] = useState(false);
1517

1618
const handleImageError = () => {
1719
setImageError(true);
1820
};
1921

22+
const handleCheckboxClick = (e: React.MouseEvent) => {
23+
e.stopPropagation();
24+
if (onToggleSelect && script.slug) {
25+
onToggleSelect(script.slug);
26+
}
27+
};
28+
2029
return (
2130
<div
22-
className="bg-card rounded-lg shadow-md hover:shadow-lg transition-shadow duration-200 cursor-pointer border border-border hover:border-primary h-full flex flex-col"
31+
className="bg-card rounded-lg shadow-md hover:shadow-lg transition-shadow duration-200 cursor-pointer border border-border hover:border-primary h-full flex flex-col relative"
2332
onClick={() => onClick(script)}
2433
>
34+
{/* Checkbox in top-left corner */}
35+
{onToggleSelect && (
36+
<div className="absolute top-2 left-2 z-10">
37+
<div
38+
className={`w-4 h-4 border-2 rounded cursor-pointer transition-all duration-200 flex items-center justify-center ${
39+
isSelected
40+
? 'bg-primary border-primary text-primary-foreground'
41+
: 'bg-card border-border hover:border-primary/60 hover:bg-accent'
42+
}`}
43+
onClick={handleCheckboxClick}
44+
>
45+
{isSelected && (
46+
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
47+
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
48+
</svg>
49+
)}
50+
</div>
51+
</div>
52+
)}
53+
2554
<div className="p-6 flex-1 flex flex-col">
2655
{/* Header with logo and name */}
2756
<div className="flex items-start space-x-4 mb-4">

src/app/_components/ScriptCardList.tsx

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,24 @@ import { TypeBadge, UpdateableBadge } from './Badge';
88
interface ScriptCardListProps {
99
script: ScriptCard;
1010
onClick: (script: ScriptCard) => void;
11+
isSelected?: boolean;
12+
onToggleSelect?: (slug: string) => void;
1113
}
1214

13-
export function ScriptCardList({ script, onClick }: ScriptCardListProps) {
15+
export function ScriptCardList({ script, onClick, isSelected = false, onToggleSelect }: ScriptCardListProps) {
1416
const [imageError, setImageError] = useState(false);
1517

1618
const handleImageError = () => {
1719
setImageError(true);
1820
};
1921

22+
const handleCheckboxClick = (e: React.MouseEvent) => {
23+
e.stopPropagation();
24+
if (onToggleSelect && script.slug) {
25+
onToggleSelect(script.slug);
26+
}
27+
};
28+
2029
const formatDate = (dateString?: string) => {
2130
if (!dateString) return 'Unknown';
2231
try {
@@ -37,10 +46,30 @@ export function ScriptCardList({ script, onClick }: ScriptCardListProps) {
3746

3847
return (
3948
<div
40-
className="bg-card rounded-lg shadow-sm hover:shadow-md transition-shadow duration-200 cursor-pointer border border-border hover:border-primary"
49+
className="bg-card rounded-lg shadow-sm hover:shadow-md transition-shadow duration-200 cursor-pointer border border-border hover:border-primary relative"
4150
onClick={() => onClick(script)}
4251
>
43-
<div className="p-6">
52+
{/* Checkbox */}
53+
{onToggleSelect && (
54+
<div className="absolute top-4 left-4 z-10">
55+
<div
56+
className={`w-4 h-4 border-2 rounded cursor-pointer transition-all duration-200 flex items-center justify-center ${
57+
isSelected
58+
? 'bg-primary border-primary text-primary-foreground'
59+
: 'bg-card border-border hover:border-primary/60 hover:bg-accent'
60+
}`}
61+
onClick={handleCheckboxClick}
62+
>
63+
{isSelected && (
64+
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
65+
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
66+
</svg>
67+
)}
68+
</div>
69+
</div>
70+
)}
71+
72+
<div className={`p-6 ${onToggleSelect ? 'pl-12' : ''}`}>
4473
<div className="flex items-start space-x-4">
4574
{/* Logo */}
4675
<div className="flex-shrink-0">

0 commit comments

Comments
 (0)