+
+
+
+
+
@@ -4895,6 +4931,9 @@
dressUpGrid.innerHTML = dressUpHtml;
if (dressUpCount) dressUpCount.textContent = `${dressUpPhotos.length} photos`;
}
+
+ // Initialize bulk action event listeners
+ setTimeout(initializeBulkActions, 100);
}
// Delete a photo from the gallery
@@ -4924,12 +4963,182 @@
showFlashMessage(`📸 Photo deleted successfully!`, 'success');
// Refresh the photo galleries
- loadLibraryContent();
+ setupLibraryGalleryTab();
console.log(`🗑️ Deleted photo ${index + 1} (${photoType})`);
}
}
+ // Update selection count and enable/disable bulk action buttons
+ function updateSelectionCount() {
+ const selectedCheckboxes = document.querySelectorAll('.photo-select:checked');
+ const count = selectedCheckboxes.length;
+
+ const selectedCountSpan = document.getElementById('selected-count');
+ const downloadBtn = document.getElementById('download-selected-photos');
+ const deleteBtn = document.getElementById('delete-selected-photos');
+
+ if (selectedCountSpan) selectedCountSpan.textContent = `${count} selected`;
+
+ if (downloadBtn) downloadBtn.disabled = count === 0;
+ if (deleteBtn) deleteBtn.disabled = count === 0;
+ }
+
+ // Select all photos
+ function selectAllPhotos() {
+ const checkboxes = document.querySelectorAll('.photo-select');
+ checkboxes.forEach(checkbox => {
+ checkbox.checked = true;
+ });
+ updateSelectionCount();
+ }
+
+ // Deselect all photos
+ function deselectAllPhotos() {
+ const checkboxes = document.querySelectorAll('.photo-select');
+ checkboxes.forEach(checkbox => {
+ checkbox.checked = false;
+ });
+ updateSelectionCount();
+ }
+
+ // Download single photo
+ function downloadSinglePhoto(index) {
+ const capturedPhotos = JSON.parse(localStorage.getItem('capturedPhotos') || '[]');
+
+ if (index < 0 || index >= capturedPhotos.length) {
+ console.error('Invalid photo index:', index);
+ return;
+ }
+
+ const photo = capturedPhotos[index];
+ const imageData = photo.imageData || photo.dataURL;
+ const timestamp = new Date(photo.timestamp || Date.now()).toISOString().slice(0, 19).replace(/:/g, '-');
+ const filename = `photo-${timestamp}.png`;
+
+ // Create download link
+ const link = document.createElement('a');
+ link.href = imageData;
+ link.download = filename;
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+
+ showFlashMessage(`📥 Photo downloaded: ${filename}`, 'success');
+ console.log(`📥 Downloaded photo: ${filename}`);
+ }
+
+ // Download selected photos (zip if multiple)
+ async function downloadSelectedPhotos() {
+ const selectedCheckboxes = document.querySelectorAll('.photo-select:checked');
+ const capturedPhotos = JSON.parse(localStorage.getItem('capturedPhotos') || '[]');
+
+ if (selectedCheckboxes.length === 0) {
+ showFlashMessage('⚠️ No photos selected for download', 'warning');
+ return;
+ }
+
+ if (selectedCheckboxes.length === 1) {
+ // Single photo download
+ const index = parseInt(selectedCheckboxes[0].dataset.index);
+ downloadSinglePhoto(index);
+ return;
+ }
+
+ // Multiple photos - create zip
+ showFlashMessage('📦 Creating zip file...', 'info');
+
+ try {
+ // Create zip file (using JSZip if available, otherwise download individually)
+ if (typeof JSZip !== 'undefined') {
+ const zip = new JSZip();
+ const timestamp = new Date().toISOString().slice(0, 19).replace(/:/g, '-');
+
+ selectedCheckboxes.forEach((checkbox, zipIndex) => {
+ const index = parseInt(checkbox.dataset.index);
+ const photo = capturedPhotos[index];
+ const imageData = photo.imageData || photo.dataURL;
+ const photoTimestamp = new Date(photo.timestamp || Date.now()).toISOString().slice(0, 19).replace(/:/g, '-');
+
+ // Convert base64 to blob
+ const base64Data = imageData.split(',')[1];
+ zip.file(`photo-${photoTimestamp}-${zipIndex + 1}.png`, base64Data, {base64: true});
+ });
+
+ const zipBlob = await zip.generateAsync({type: 'blob'});
+ const link = document.createElement('a');
+ link.href = URL.createObjectURL(zipBlob);
+ link.download = `photos-${timestamp}.zip`;
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+
+ showFlashMessage(`📥 Downloaded ${selectedCheckboxes.length} photos as zip file`, 'success');
+ } else {
+ // Fallback: download individually
+ selectedCheckboxes.forEach((checkbox, downloadIndex) => {
+ const index = parseInt(checkbox.dataset.index);
+ setTimeout(() => {
+ downloadSinglePhoto(index);
+ }, downloadIndex * 100); // Stagger downloads
+ });
+
+ showFlashMessage(`📥 Downloading ${selectedCheckboxes.length} photos individually`, 'info');
+ }
+ } catch (error) {
+ console.error('Download error:', error);
+ showFlashMessage('❌ Error creating download', 'error');
+ }
+ }
+
+ // Delete selected photos
+ function deleteSelectedPhotos() {
+ const selectedCheckboxes = document.querySelectorAll('.photo-select:checked');
+
+ if (selectedCheckboxes.length === 0) {
+ showFlashMessage('⚠️ No photos selected for deletion', 'warning');
+ return;
+ }
+
+ const confirmed = confirm(`Are you sure you want to delete ${selectedCheckboxes.length} selected photos?\n\nThis action cannot be undone.`);
+
+ if (confirmed) {
+ const capturedPhotos = JSON.parse(localStorage.getItem('capturedPhotos') || '[]');
+ const indicesToDelete = Array.from(selectedCheckboxes).map(cb => parseInt(cb.dataset.index)).sort((a, b) => b - a);
+
+ // Delete in reverse order to maintain indices
+ indicesToDelete.forEach(index => {
+ if (index >= 0 && index < capturedPhotos.length) {
+ capturedPhotos.splice(index, 1);
+ }
+ });
+
+ // Update localStorage
+ localStorage.setItem('capturedPhotos', JSON.stringify(capturedPhotos));
+
+ // Show success message
+ showFlashMessage(`🗑️ Successfully deleted ${indicesToDelete.length} photos!`, 'success');
+
+ // Refresh the photo galleries
+ setupLibraryGalleryTab();
+
+ console.log(`🗑️ Bulk deleted ${indicesToDelete.length} photos`);
+ }
+ }
+
+ // Initialize bulk action event listeners
+ function initializeBulkActions() {
+ const selectAllBtn = document.getElementById('select-all-photos');
+ const deselectAllBtn = document.getElementById('deselect-all-photos');
+ const downloadSelectedBtn = document.getElementById('download-selected-photos');
+ const deleteSelectedBtn = document.getElementById('delete-selected-photos');
+
+ if (selectAllBtn) selectAllBtn.addEventListener('click', selectAllPhotos);
+ if (deselectAllBtn) deselectAllBtn.addEventListener('click', deselectAllPhotos);
+ if (downloadSelectedBtn) downloadSelectedBtn.addEventListener('click', downloadSelectedPhotos);
+ if (deleteSelectedBtn) deleteSelectedBtn.addEventListener('click', deleteSelectedPhotos);
+ }
+
// Show photo preview in modal
function showPhotoPreview(imageData, title) {
// Create modal overlay
diff --git a/src/styles/styles.css b/src/styles/styles.css
index e6659b2..1126d36 100644
--- a/src/styles/styles.css
+++ b/src/styles/styles.css
@@ -6510,6 +6510,155 @@ button#start-mirror-btn:disabled {
transform: scale(0.95);
}
+.photo-download-btn {
+ background: rgba(52, 152, 219, 0.9);
+ color: white;
+ border: none;
+ border-radius: 50%;
+ width: 32px;
+ height: 32px;
+ font-size: 14px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.3s ease;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
+}
+
+.photo-download-btn:hover {
+ background: rgba(41, 128, 185, 1);
+ transform: scale(1.1);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
+}
+
+.photo-download-btn:active {
+ transform: scale(0.95);
+}
+
+.photo-checkbox {
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ z-index: 10;
+}
+
+.photo-checkbox input[type="checkbox"] {
+ width: 20px;
+ height: 20px;
+ cursor: pointer;
+ opacity: 0;
+ position: absolute;
+}
+
+.checkbox-label {
+ width: 20px;
+ height: 20px;
+ border: 2px solid rgba(255, 255, 255, 0.8);
+ border-radius: 3px;
+ background: rgba(0, 0, 0, 0.6);
+ display: block;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ position: relative;
+}
+
+.checkbox-label::after {
+ content: '✓';
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%) scale(0);
+ color: white;
+ font-size: 14px;
+ font-weight: bold;
+ transition: transform 0.2s ease;
+}
+
+.photo-checkbox input:checked + .checkbox-label {
+ background: rgba(46, 204, 113, 0.9);
+ border-color: rgba(46, 204, 113, 1);
+}
+
+.photo-checkbox input:checked + .checkbox-label::after {
+ transform: translate(-50%, -50%) scale(1);
+}
+
+.bulk-actions {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 12px;
+ background: rgba(0, 0, 0, 0.1);
+ border-radius: 8px;
+ margin-bottom: 16px;
+ gap: 16px;
+}
+
+.selection-controls {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.action-buttons {
+ display: flex;
+ gap: 8px;
+}
+
+.selected-count {
+ color: var(--text-secondary);
+ font-size: var(--font-sm);
+ font-weight: 500;
+}
+
+.btn-small {
+ padding: 6px 12px;
+ font-size: var(--font-sm);
+}
+
+.btn-success {
+ background: var(--color-success);
+ color: white;
+ border: none;
+ padding: 8px 16px;
+ border-radius: 6px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+}
+
+.btn-success:hover:not(:disabled) {
+ background: #27ae60;
+ transform: translateY(-1px);
+}
+
+.btn-success:disabled {
+ background: var(--bg-secondary);
+ color: var(--text-muted);
+ cursor: not-allowed;
+}
+
+.btn-danger {
+ background: var(--color-error);
+ color: white;
+ border: none;
+ padding: 8px 16px;
+ border-radius: 6px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+}
+
+.btn-danger:hover:not(:disabled) {
+ background: #c0392b;
+ transform: translateY(-1px);
+}
+
+.btn-danger:disabled {
+ background: var(--bg-secondary);
+ color: var(--text-muted);
+ cursor: not-allowed;
+}
+
.photo-info {
padding: 12px;
background: var(--bg-tertiary);