/
/
/
1import { useState, useCallback } from 'react';
2import type { BatchActionsProps } from '../api/types';
3
4export function BatchActions({
5 selectedFilenames,
6 selectedCount,
7 totalCount,
8 onDownloadSelected,
9 onDeleteSelected,
10 onClearSelection,
11 isLoading
12}: BatchActionsProps) {
13 const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
14 const [isProcessing, setIsProcessing] = useState(false);
15
16 const handleDownload = useCallback(async () => {
17 if (selectedCount === 0) return;
18
19 try {
20 setIsProcessing(true);
21 await onDownloadSelected();
22 } catch (error) {
23 console.error('Batch download failed:', error);
24 } finally {
25 setIsProcessing(false);
26 }
27 }, [selectedCount, onDownloadSelected]);
28
29 const handleDeleteConfirm = useCallback(async () => {
30 if (selectedCount === 0) return;
31
32 try {
33 setIsProcessing(true);
34 await onDeleteSelected();
35 setShowDeleteConfirm(false);
36 } catch (error) {
37 console.error('Batch delete failed:', error);
38 } finally {
39 setIsProcessing(false);
40 }
41 }, [selectedCount, onDeleteSelected]);
42
43 const handleDeleteClick = useCallback(() => {
44 setShowDeleteConfirm(true);
45 }, []);
46
47 const handleCancelDelete = useCallback(() => {
48 setShowDeleteConfirm(false);
49 }, []);
50
51 if (selectedCount === 0) {
52 return null;
53 }
54
55 const isDisabled = isLoading || isProcessing;
56
57 return (
58 <>
59 <div className="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-4">
60 <div className="flex items-center justify-between">
61 <div className="flex items-center space-x-4">
62 <span className="text-sm font-medium text-blue-900">
63 {selectedCount} of {totalCount} recording{selectedCount !== 1 ? 's' : ''} selected
64 </span>
65
66 <button
67 onClick={onClearSelection}
68 className="text-xs text-blue-600 hover:text-blue-800 underline"
69 disabled={isDisabled}
70 >
71 Clear Selection
72 </button>
73 </div>
74
75 <div className="flex items-center space-x-3">
76 <button
77 onClick={handleDownload}
78 disabled={isDisabled}
79 className="px-4 py-2 text-sm bg-green-600 text-white rounded-md hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors flex items-center space-x-2"
80 >
81 {isProcessing ? (
82 <>
83 <div className="animate-spin h-4 w-4 border-2 border-white border-t-transparent rounded-full"></div>
84 <span>Downloading...</span>
85 </>
86 ) : (
87 <>
88 <svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
89 <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
90 </svg>
91 <span>Download Selected</span>
92 </>
93 )}
94 </button>
95
96 <button
97 onClick={handleDeleteClick}
98 disabled={isDisabled}
99 className="px-4 py-2 text-sm bg-red-600 text-white rounded-md hover:bg-red-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors flex items-center space-x-2"
100 >
101 <svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
102 <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
103 </svg>
104 <span>Delete Selected</span>
105 </button>
106 </div>
107 </div>
108 </div>
109
110 {/* Delete Confirmation Modal */}
111 {showDeleteConfirm && (
112 <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
113 <div className="bg-white rounded-lg p-6 max-w-md w-full mx-4">
114 <div className="flex items-center space-x-3 mb-4">
115 <div className="flex-shrink-0">
116 <svg className="h-6 w-6 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
117 <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z" />
118 </svg>
119 </div>
120 <div>
121 <h3 className="text-lg font-medium text-gray-900">Confirm Deletion</h3>
122 <p className="text-sm text-gray-500">This action cannot be undone.</p>
123 </div>
124 </div>
125
126 <div className="mb-6">
127 <p className="text-sm text-gray-700">
128 Are you sure you want to delete {selectedCount} recording{selectedCount !== 1 ? 's' : ''}?
129 </p>
130
131 {selectedCount <= 5 && (
132 <div className="mt-3">
133 <p className="text-xs text-gray-600 font-medium mb-1">Files to be deleted:</p>
134 <ul className="text-xs text-gray-600 space-y-1">
135 {selectedFilenames.map(filename => (
136 <li key={filename} className="font-mono bg-gray-50 px-2 py-1 rounded">
137 {filename}
138 </li>
139 ))}
140 </ul>
141 </div>
142 )}
143 </div>
144
145 <div className="flex justify-end space-x-3">
146 <button
147 onClick={handleCancelDelete}
148 disabled={isProcessing}
149 className="px-4 py-2 text-sm text-gray-700 bg-gray-200 rounded-md hover:bg-gray-300 disabled:opacity-50 transition-colors"
150 >
151 Cancel
152 </button>
153 <button
154 onClick={handleDeleteConfirm}
155 disabled={isProcessing}
156 className="px-4 py-2 text-sm bg-red-600 text-white rounded-md hover:bg-red-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors flex items-center space-x-2"
157 >
158 {isProcessing ? (
159 <>
160 <div className="animate-spin h-4 w-4 border-2 border-white border-t-transparent rounded-full"></div>
161 <span>Deleting...</span>
162 </>
163 ) : (
164 <span>Delete {selectedCount} Recording{selectedCount !== 1 ? 's' : ''}</span>
165 )}
166 </button>
167 </div>
168 </div>
169 </div>
170 )}
171 </>
172 );
173}