diff --git a/index.html b/index.html index 8f37f1a..62f20eb 100644 --- a/index.html +++ b/index.html @@ -1574,6 +1574,17 @@

📸 All Photos

0 photos +
+
+ + + 0 selected +
+
+ + +
+

📸 No photos found

@@ -1587,6 +1598,17 @@

👗 Dress Up Photos

0 photos
+
+
+ + + 0 selected +
+
+ + +
+

👗 No dress up photos found

@@ -4835,9 +4857,16 @@ photosHtml += `
+
+ + +
Captured Photo ${index + 1}
+ @@ -4876,9 +4905,16 @@ dressUpHtml += `
+
+ + +
Dress Up Photo ${index + 1}
+ @@ -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);