Reservasi-Cafe/resources/views/admin/gallery/index.blade.php

423 lines
21 KiB
PHP

@extends('layouts.admin.app')
@section('content')
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
@if(session('success'))
<div id="alert-success" class="flex items-center p-4 mb-4 text-green-800 rounded-lg bg-green-50" role="alert">
<i class="fas fa-check-circle flex-shrink-0 w-4 h-4"></i>
<div class="ms-3 text-sm font-medium">
{{ session('success') }}
</div>
<button type="button" class="ms-auto -mx-1.5 -my-1.5 bg-green-50 text-green-500 rounded-lg focus:ring-2 focus:ring-green-400 p-1.5 hover:bg-green-200 inline-flex items-center justify-center h-8 w-8" data-dismiss-target="#alert-success" aria-label="Close">
<span class="sr-only">Close</span>
<i class="fas fa-times w-3 h-3"></i>
</button>
</div>
@endif
<!-- Add Category Button -->
<div class="mb-4">
<button type="button" id="addCategoryBtn" class="text-white bg-red-600 hover:bg-red-700 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 border border-red-700 shadow-sm">
<i class="fas fa-plus-circle mr-2"></i>Add New Gallery Category
</button>
</div>
<!-- Gallery Categories Section -->
<div class="bg-white dark:bg-gray-800 shadow-md rounded-lg mb-8">
<div class="p-6">
<h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-6">
Gallery Categories
</h3>
<!-- Categories Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
@foreach($categories as $category)
<div class="bg-white dark:bg-gray-700 rounded-lg shadow-md">
<div class="relative">
<img src="{{ asset('storage/' . $category->thumbnail) }}" alt="{{ $category->name }}" class="w-full h-48 object-cover rounded-t-lg">
<div class="absolute bottom-0 left-0 right-0 p-4 bg-gradient-to-t from-black/70 to-transparent">
<h4 class="text-lg font-semibold text-white">{{ $category->name }}</h4>
</div>
</div>
<div class="p-4">
<p class="text-sm text-gray-600 dark:text-gray-300 mb-4">{{ $category->description }}</p>
<div class="flex items-center justify-between">
<button onclick="editCategory({{ $category->id }})" class="text-blue-600 hover:text-blue-800 flex items-center bg-blue-50 px-3 py-1 rounded-md border border-blue-200 hover:bg-blue-100">
<i class="fas fa-edit mr-1"></i>
<span>Edit</span>
</button>
<button onclick="deleteCategory({{ $category->id }})" class="text-red-600 hover:text-red-800 flex items-center bg-red-50 px-3 py-1 rounded-md border border-red-200 hover:bg-red-100">
<i class="fas fa-trash-alt mr-1"></i>
<span>Delete</span>
</button>
</div>
</div>
</div>
@endforeach
</div>
</div>
</div>
<!-- Category Images Sections -->
@foreach($categories as $category)
<div class="bg-white dark:bg-gray-800 shadow-md rounded-lg mb-8">
<div class="p-6">
<div class="flex justify-between items-center mb-6">
<div>
<h3 class="text-xl font-semibold text-gray-900 dark:text-white">{{ $category->name }}</h3>
<p class="text-sm text-gray-500 mt-1">{{ $category->images->count() }} images</p>
</div>
<button onclick="addImage({{ $category->id }})" class="text-white bg-red-600 hover:bg-red-700 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-4 py-2 border border-red-700 shadow-sm">
<i class="fas fa-plus-circle mr-2"></i>Add Image
</button>
</div>
@if($category->images->isEmpty())
<div class="text-center py-8 text-gray-500">
<i class="fas fa-images text-4xl mb-2"></i>
<p>No images yet in this category.</p>
<p class="text-sm mt-2">Click the "Add Image" button to add your first image.</p>
</div>
@else
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
@foreach($category->images as $image)
<div class="relative group">
<img src="{{ asset('storage/' . $image->image) }}" alt="{{ $image->caption }}" class="w-full h-48 object-cover rounded-lg">
<div class="absolute inset-0 bg-black bg-opacity-50 opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-lg flex items-center justify-center space-x-4">
<button onclick="editImage({{ $image->id }})" class="text-white hover:text-blue-400 flex items-center bg-blue-600 hover:bg-blue-700 px-3 py-1 rounded-md">
<i class="fas fa-edit mr-1"></i>
<span>Edit</span>
</button>
<button onclick="deleteImage({{ $image->id }})" class="text-white hover:text-red-400 flex items-center bg-red-600 hover:bg-red-700 px-3 py-1 rounded-md">
<i class="fas fa-trash-alt mr-1"></i>
<span>Delete</span>
</button>
</div>
@if($image->caption)
<div class="absolute bottom-0 left-0 right-0 p-2 bg-black bg-opacity-50 text-white text-sm">
{{ $image->caption }}
</div>
@endif
</div>
@endforeach
</div>
@endif
</div>
</div>
@endforeach
</div>
</div>
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
// Auto-hide alerts after 3 seconds
document.addEventListener('DOMContentLoaded', function() {
setTimeout(function() {
const alerts = document.querySelectorAll('[role="alert"]');
alerts.forEach(alert => {
alert.style.display = 'none';
});
}, 3000);
});
// Add New Category
document.getElementById('addCategoryBtn').addEventListener('click', function() {
Swal.fire({
title: 'Add New Gallery Category',
html: `
<form id="addCategoryForm" class="space-y-4">
<div class="text-left">
<label class="block text-sm font-medium text-gray-700">Category Name</label>
<input type="text" id="name" name="name" required class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-maroon focus:ring-maroon">
</div>
<div class="text-left">
<label class="block text-sm font-medium text-gray-700">Description</label>
<textarea id="description" name="description" required rows="3" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-maroon focus:ring-maroon"></textarea>
</div>
<div class="text-left">
<label class="block text-sm font-medium text-gray-700">Thumbnail</label>
<input type="file" id="thumbnail" name="thumbnail" accept="image/*" required class="mt-1 block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-maroon file:text-white hover:file:bg-red-800">
<p class="mt-1 text-sm text-gray-500">Recommended size: 800x600 pixels</p>
</div>
</form>
`,
showCancelButton: true,
confirmButtonText: 'Add Category',
confirmButtonColor: '#800000',
preConfirm: () => {
const formData = new FormData(document.getElementById('addCategoryForm'));
return fetch('{{ route("admin.gallery.store-category") }}', {
method: 'POST',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: formData
})
.then(response => response.json())
.catch(error => {
Swal.showValidationMessage(`Request failed: ${error}`)
})
}
}).then((result) => {
if (result.isConfirmed && result.value.success) {
Swal.fire('Success!', result.value.message, 'success')
.then(() => {
location.reload();
});
} else if (result.value && !result.value.success) {
Swal.fire('Error!', result.value.message, 'error');
}
});
});
// Edit Category
function editCategory(id) {
fetch(`/admin/gallery/category/${id}`)
.then(response => response.json())
.then(data => {
if (data.success) {
const category = data.data;
Swal.fire({
title: 'Edit Gallery Category',
html: `
<form id="editCategoryForm" class="space-y-4">
<input type="hidden" name="_method" value="PUT">
<div class="text-left">
<label class="block text-sm font-medium text-gray-700">Category Name</label>
<input type="text" id="edit_name" name="name" value="${category.name}" required class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-maroon focus:ring-maroon">
</div>
<div class="text-left">
<label class="block text-sm font-medium text-gray-700">Description</label>
<textarea id="edit_description" name="description" required rows="3" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-maroon focus:ring-maroon">${category.description}</textarea>
</div>
<div class="text-left">
<label class="block text-sm font-medium text-gray-700">Current Thumbnail</label>
<img src="/storage/${category.thumbnail}" class="w-32 h-32 object-cover rounded-lg mb-2">
<label class="block text-sm font-medium text-gray-700">New Thumbnail (Optional)</label>
<input type="file" id="edit_thumbnail" name="thumbnail" accept="image/*" class="mt-1 block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-maroon file:text-white hover:file:bg-red-800">
<p class="mt-1 text-sm text-gray-500">Leave empty to keep current thumbnail</p>
</div>
</form>
`,
showCancelButton: true,
confirmButtonText: 'Save Changes',
confirmButtonColor: '#800000',
preConfirm: () => {
const formData = new FormData(document.getElementById('editCategoryForm'));
return fetch(`/admin/gallery/category/${id}`, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: formData
})
.then(response => response.json())
.catch(error => {
Swal.showValidationMessage(`Request failed: ${error}`)
})
}
}).then((result) => {
if (result.isConfirmed && result.value.success) {
Swal.fire('Success!', result.value.message, 'success')
.then(() => {
location.reload();
});
} else if (result.value && !result.value.success) {
Swal.fire('Error!', result.value.message, 'error');
}
});
} else {
Swal.fire('Error!', 'Failed to fetch category data', 'error');
}
})
.catch(error => {
Swal.fire('Error!', 'Failed to fetch category data', 'error');
});
}
// Delete Category
function deleteCategory(id) {
Swal.fire({
title: 'Delete Category?',
text: "This will delete all images in this category. This action cannot be undone!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#800000',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Yes, delete it!',
cancelButtonText: 'Cancel'
}).then((result) => {
if (result.isConfirmed) {
fetch(`/admin/gallery/category/${id}`, {
method: 'DELETE',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
Swal.fire('Deleted!', data.message, 'success')
.then(() => {
location.reload();
});
} else {
Swal.fire('Error!', data.message, 'error');
}
})
.catch(error => {
Swal.fire('Error!', 'Something went wrong.', 'error');
});
}
});
}
// Add Image
function addImage(categoryId) {
Swal.fire({
title: 'Add New Image',
html: `
<form id="addImageForm" class="space-y-4">
<div class="text-left">
<label class="block text-sm font-medium text-gray-700">Image</label>
<input type="file" id="image" name="image" accept="image/*" required class="mt-1 block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-maroon file:text-white hover:file:bg-red-800">
<p class="mt-1 text-sm text-gray-500">Recommended size: 1200x800 pixels</p>
</div>
<div class="text-left">
<label class="block text-sm font-medium text-gray-700">Caption (Optional)</label>
<input type="text" id="caption" name="caption" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-maroon focus:ring-maroon">
<p class="mt-1 text-sm text-gray-500">A short description of the image</p>
</div>
</form>
`,
showCancelButton: true,
confirmButtonText: 'Add Image',
confirmButtonColor: '#800000',
preConfirm: () => {
const formData = new FormData(document.getElementById('addImageForm'));
return fetch(`/admin/gallery/category/${categoryId}/image`, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: formData
})
.then(response => response.json())
.catch(error => {
Swal.showValidationMessage(`Request failed: ${error}`)
})
}
}).then((result) => {
if (result.isConfirmed && result.value.success) {
Swal.fire('Success!', result.value.message, 'success')
.then(() => {
location.reload();
});
} else if (result.value && !result.value.success) {
Swal.fire('Error!', result.value.message, 'error');
}
});
}
// Edit Image
function editImage(id) {
fetch(`/admin/gallery/image/${id}`)
.then(response => response.json())
.then(data => {
if (data.success) {
const image = data.data;
Swal.fire({
title: 'Edit Image',
html: `
<form id="editImageForm" class="space-y-4">
<input type="hidden" name="_method" value="PUT">
<div class="text-left">
<label class="block text-sm font-medium text-gray-700">Current Image</label>
<img src="/storage/${image.image}" class="w-full h-48 object-cover rounded-lg mb-2">
<label class="block text-sm font-medium text-gray-700">New Image (Optional)</label>
<input type="file" id="edit_image" name="image" accept="image/*" class="mt-1 block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-maroon file:text-white hover:file:bg-red-800">
<p class="mt-1 text-sm text-gray-500">Leave empty to keep current image</p>
</div>
<div class="text-left">
<label class="block text-sm font-medium text-gray-700">Caption</label>
<input type="text" id="edit_caption" name="caption" value="${image.caption || ''}" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-maroon focus:ring-maroon">
</div>
</form>
`,
showCancelButton: true,
confirmButtonText: 'Save Changes',
confirmButtonColor: '#800000',
preConfirm: () => {
const formData = new FormData(document.getElementById('editImageForm'));
return fetch(`/admin/gallery/image/${id}`, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: formData
})
.then(response => response.json())
.catch(error => {
Swal.showValidationMessage(`Request failed: ${error}`)
})
}
}).then((result) => {
if (result.isConfirmed && result.value.success) {
Swal.fire('Success!', result.value.message, 'success')
.then(() => {
location.reload();
});
} else if (result.value && !result.value.success) {
Swal.fire('Error!', result.value.message, 'error');
}
});
} else {
Swal.fire('Error!', 'Failed to fetch image data', 'error');
}
})
.catch(error => {
Swal.fire('Error!', 'Failed to fetch image data', 'error');
});
}
// Delete Image
function deleteImage(id) {
Swal.fire({
title: 'Delete Image?',
text: "You won't be able to revert this!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#800000',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Yes, delete it!',
cancelButtonText: 'Cancel'
}).then((result) => {
if (result.isConfirmed) {
fetch(`/admin/gallery/image/${id}`, {
method: 'DELETE',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
Swal.fire('Deleted!', data.message, 'success')
.then(() => {
location.reload();
});
} else {
Swal.fire('Error!', data.message, 'error');
}
})
.catch(error => {
Swal.fire('Error!', 'Something went wrong.', 'error');
});
}
});
}
</script>
@endpush
@endsection