264 lines
14 KiB
PHP
264 lines
14 KiB
PHP
@extends('layouts.app')
|
|
|
|
@section('title', 'Semua Foto')
|
|
|
|
@section('content')
|
|
<div class="py-8 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div class="bg-white rounded-lg shadow-lg overflow-hidden">
|
|
<div class="p-6">
|
|
<div class="flex justify-between items-center mb-6">
|
|
<h2 class="text-2xl font-bold text-gray-800">Semua Foto ESP32-CAM</h2>
|
|
<a href="{{ route('dashboard') }}" class="px-4 py-2 bg-gray-500 text-white rounded-md hover:bg-gray-600 transition duration-300">
|
|
Kembali ke Dashboard
|
|
</a>
|
|
</div>
|
|
|
|
@if(count($photos) > 0)
|
|
<div id="photos-grid" class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
|
@foreach($photos as $photo)
|
|
<div class="bg-gray-50 rounded-lg overflow-hidden shadow-sm hover:shadow-md transition duration-300 photo-item">
|
|
<div class="relative pt-[75%]">
|
|
<img src="{{ asset($photo['path']) }}"
|
|
alt="{{ $photo['name'] }}"
|
|
class="absolute inset-0 w-full h-full object-cover"
|
|
onclick="openPhotoModal('{{ asset($photo['path']) }}', '{{ $photo['time'] }}', '{{ $photo['size'] }}', '{{ $photo['name'] }}')">
|
|
<div class="absolute top-2 right-2">
|
|
<button class="delete-photo p-1.5 bg-red-500 text-white rounded-full hover:bg-red-600 focus:outline-none"
|
|
data-filename="{{ $photo['name'] }}">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" viewBox="0 0 20 20" fill="currentColor">
|
|
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="p-3">
|
|
<p class="text-gray-600 text-sm mb-1">
|
|
<span class="font-semibold">Waktu:</span> {{ $photo['time'] }}
|
|
</p>
|
|
<p class="text-gray-600 text-sm">
|
|
<span class="font-semibold">Ukuran:</span> {{ $photo['size'] }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
@else
|
|
<div class="text-center py-12 bg-gray-50 rounded-lg">
|
|
<svg class="w-16 h-16 text-gray-400 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
|
</svg>
|
|
<p class="text-gray-500 text-lg">Belum ada foto yang tersedia</p>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@push('scripts')
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Konfigurasi SweetAlert2 untuk menggunakan warna Tailwind
|
|
const mySwal = Swal.mixin({
|
|
customClass: {
|
|
confirmButton: 'bg-blue-600 text-white font-medium px-4 py-2 rounded-md shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 mr-2',
|
|
cancelButton: 'bg-red-500 text-white font-medium px-4 py-2 rounded-md shadow-sm hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-500'
|
|
},
|
|
buttonsStyling: false
|
|
});
|
|
|
|
// Tambahkan event listener untuk tombol hapus
|
|
document.querySelectorAll('.delete-photo').forEach(button => {
|
|
button.addEventListener('click', function() {
|
|
const filename = this.getAttribute('data-filename');
|
|
const photoElement = this.closest('.photo-item');
|
|
|
|
mySwal.fire({
|
|
title: 'Apakah Anda yakin?',
|
|
text: "Foto yang dihapus tidak dapat dikembalikan!",
|
|
icon: 'warning',
|
|
showCancelButton: true,
|
|
confirmButtonText: 'Ya, hapus!',
|
|
cancelButtonText: 'Batal'
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
// Tampilkan loading
|
|
mySwal.fire({
|
|
title: 'Menghapus...',
|
|
html: 'Mohon tunggu sebentar...',
|
|
allowOutsideClick: false,
|
|
didOpen: () => {
|
|
mySwal.showLoading();
|
|
}
|
|
});
|
|
|
|
// Kirim permintaan AJAX untuk menghapus foto
|
|
fetch('/api/esp32cam/delete', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
|
},
|
|
body: JSON.stringify({
|
|
filename: filename,
|
|
delete_storage: true // Tambahkan flag untuk menghapus file di storage
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.status === 'success') {
|
|
// Tambahkan animasi sebelum menghapus
|
|
photoElement.style.transition = 'all 0.5s ease';
|
|
photoElement.style.opacity = '0';
|
|
photoElement.style.transform = 'scale(0.8)';
|
|
|
|
setTimeout(() => {
|
|
photoElement.remove();
|
|
|
|
// Periksa apakah masih ada foto
|
|
if (document.querySelectorAll('.photo-item').length === 0) {
|
|
const photosGrid = document.getElementById('photos-grid');
|
|
photosGrid.innerHTML = `
|
|
<div class="text-center py-12 bg-gray-50 rounded-lg col-span-full">
|
|
<svg class="w-16 h-16 text-gray-400 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
|
</svg>
|
|
<p class="text-gray-500 text-lg">Belum ada foto yang tersedia</p>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
mySwal.fire({
|
|
title: 'Terhapus!',
|
|
text: 'Foto berhasil dihapus.',
|
|
icon: 'success',
|
|
timer: 1500,
|
|
showConfirmButton: false
|
|
});
|
|
}, 500);
|
|
} else {
|
|
mySwal.fire({
|
|
title: 'Error!',
|
|
text: 'Gagal menghapus foto: ' + data.message,
|
|
icon: 'error'
|
|
});
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
mySwal.fire({
|
|
title: 'Error!',
|
|
text: 'Terjadi kesalahan saat menghapus foto.',
|
|
icon: 'error'
|
|
});
|
|
});
|
|
}
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
// Variabel untuk zoom
|
|
let currentScale = 1;
|
|
|
|
// Fungsi zoom
|
|
function zoomIn() {
|
|
currentScale += 0.25;
|
|
document.getElementById('modal-photo').style.transform = `scale(${currentScale})`;
|
|
}
|
|
|
|
function zoomOut() {
|
|
if (currentScale > 0.5) {
|
|
currentScale -= 0.25;
|
|
document.getElementById('modal-photo').style.transform = `scale(${currentScale})`;
|
|
}
|
|
}
|
|
|
|
function zoomReset() {
|
|
currentScale = 1;
|
|
document.getElementById('modal-photo').style.transform = 'scale(1)';
|
|
}
|
|
|
|
// Fungsi untuk modal foto
|
|
function openPhotoModal(src, timestamp, filesize, filename) {
|
|
const modal = document.getElementById('photoModal');
|
|
const modalPhoto = document.getElementById('modal-photo');
|
|
const modalTimestamp = document.getElementById('modal-timestamp');
|
|
const modalFilesize = document.getElementById('modal-filesize');
|
|
const modalFilename = document.getElementById('modal-filename');
|
|
|
|
modalPhoto.src = src;
|
|
modalTimestamp.textContent = timestamp;
|
|
modalFilesize.textContent = filesize;
|
|
modalFilename.textContent = filename;
|
|
|
|
// Reset zoom setiap kali membuka modal
|
|
currentScale = 1;
|
|
modalPhoto.style.transform = 'scale(1)';
|
|
|
|
modal.classList.remove('hidden');
|
|
}
|
|
|
|
function closePhotoModal() {
|
|
const modal = document.getElementById('photoModal');
|
|
modal.classList.add('hidden');
|
|
}
|
|
|
|
// Tutup modal dengan escape key
|
|
document.addEventListener('keydown', function(e) {
|
|
if (e.key === 'Escape') {
|
|
closePhotoModal();
|
|
}
|
|
});
|
|
|
|
// Tutup modal dengan click di luar content
|
|
document.getElementById('photoModal')?.addEventListener('click', function(e) {
|
|
if (e.target === this) {
|
|
closePhotoModal();
|
|
}
|
|
});
|
|
</script>
|
|
@endpush
|
|
|
|
<!-- Modal untuk Photo Zoom -->
|
|
<div id="photoModal" class="fixed inset-0 z-50 hidden overflow-auto bg-black bg-opacity-80 flex items-center justify-center p-4">
|
|
<div class="relative bg-white rounded-lg shadow-xl max-w-4xl w-full">
|
|
<div class="flex justify-between items-center p-4 border-b">
|
|
<h3 class="text-lg font-semibold text-gray-900" id="modal-title">Detail Foto</h3>
|
|
<button type="button" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center" onclick="closePhotoModal()">
|
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
|
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<div class="p-4 space-y-6">
|
|
<div class="relative">
|
|
<div id="zoom-container" class="overflow-hidden relative">
|
|
<img id="modal-photo" src="" alt="Photo" class="w-full transform transition-transform duration-300">
|
|
</div>
|
|
<div class="flex justify-center mt-4 space-x-2">
|
|
<button id="zoom-in" class="bg-gray-200 p-2 rounded-full hover:bg-gray-300 transition-colors" onclick="zoomIn()">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
|
<path fill-rule="evenodd" d="M10 5a1 1 0 011 1v3h3a1 1 0 110 2h-3v3a1 1 0 11-2 0v-3H6a1 1 0 110-2h3V6a1 1 0 011-1z" clip-rule="evenodd" />
|
|
</svg>
|
|
</button>
|
|
<button id="zoom-out" class="bg-gray-200 p-2 rounded-full hover:bg-gray-300 transition-colors" onclick="zoomOut()">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
|
<path fill-rule="evenodd" d="M5 10a1 1 0 011-1h8a1 1 0 110 2H6a1 1 0 01-1-1z" clip-rule="evenodd" />
|
|
</svg>
|
|
</button>
|
|
<button id="zoom-reset" class="bg-gray-200 p-2 rounded-full hover:bg-gray-300 transition-colors" onclick="zoomReset()">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
|
<path fill-rule="evenodd" d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z" clip-rule="evenodd" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="mt-4">
|
|
<p class="text-sm text-gray-600"><span class="font-medium">Nama File:</span> <span id="modal-filename"></span></p>
|
|
<p class="text-sm text-gray-600"><span class="font-medium">Waktu Pengambilan:</span> <span id="modal-timestamp"></span></p>
|
|
<p class="text-sm text-gray-600"><span class="font-medium">Ukuran File:</span> <span id="modal-filesize"></span></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endsection
|