MIF_E31221305/TA_website/resources/views/admin/specializations/index.blade.php

497 lines
24 KiB
PHP

@extends('admin.layouts.app')
@section('title', 'Spesialisasi')
@section('content')
<div class="p-6">
<!-- Header -->
<div class="flex flex-col md:flex-row md:justify-between md:items-center gap-4 mb-6">
<h2 class="text-2xl font-semibold text-gray-800">Daftar Spesialisasi</h2>
<!-- Search and Add Button -->
<div class="flex flex-wrap items-center gap-3">
<div class="relative">
<input
type="text"
id="searchInput"
placeholder="Cari spesialisasi..."
class="w-full md:w-64 pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
<svg class="w-5 h-5 text-gray-400 absolute left-3 top-2.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
</svg>
</div>
<button onclick="showAddModal()" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg flex items-center gap-2">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"/>
</svg>
Tambah Spesialisasi
</button>
</div>
</div>
<!-- Specializations Grid -->
<div id="specializationsGrid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Specializations will be loaded here -->
</div>
</div>
<!-- Add Specialization Modal -->
<div id="addSpecializationModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden z-50">
<div class="min-h-screen px-4 text-center">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
<!-- Modal panel -->
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div class="w-full">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg leading-6 font-medium text-gray-900">
Tambah Spesialisasi Baru
</h3>
<button type="button" onclick="closeAddModal()" class="text-gray-400 hover:text-gray-500">
<span class="sr-only">Close</span>
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<form id="addSpecializationForm" onsubmit="addSpecialization(event)">
<div class="space-y-4">
<div>
<label for="name" class="block text-sm font-medium text-gray-700">Nama Spesialisasi</label>
<input type="text" name="name" id="name" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
</div>
<div>
<label for="category" class="block text-sm font-medium text-gray-700">Kategori</label>
<select name="category" id="category" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
<option value="">Pilih Kategori</option>
<option value="Gaya Busana">Gaya Busana</option>
<option value="Jenis Layanan">Jenis Layanan</option>
<option value="Hiasan Busana">Hiasan Busana</option>
</select>
</div>
<div>
<label for="photo" class="block text-sm font-medium text-gray-700">Foto</label>
<input type="file" name="photo" id="photo" 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-md file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100">
</div>
</div>
<div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<button type="submit"
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">
Simpan
</button>
<button type="button" onclick="closeAddModal()"
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:mt-0 sm:w-auto sm:text-sm">
Batal
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Edit Specialization Modal -->
<div id="editSpecializationModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden z-50">
<div class="min-h-screen px-4 text-center">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
<!-- Modal panel -->
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div class="w-full">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg leading-6 font-medium text-gray-900">
Edit Spesialisasi
</h3>
<button type="button" onclick="closeEditModal()" class="text-gray-400 hover:text-gray-500">
<span class="sr-only">Close</span>
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<form id="editSpecializationForm" onsubmit="updateSpecialization(event)">
<input type="hidden" id="edit_specialization_id">
<div class="space-y-4">
<div>
<label for="edit_name" class="block text-sm font-medium text-gray-700">Nama Spesialisasi</label>
<input type="text" name="name" id="edit_name" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
</div>
<div>
<label for="edit_category" class="block text-sm font-medium text-gray-700">Kategori</label>
<select name="category" id="edit_category" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
<option value="">Pilih Kategori</option>
<option value="Gaya Busana">Gaya Busana</option>
<option value="Jenis Layanan">Jenis Layanan</option>
<option value="Hiasan Busana">Hiasan Busana</option>
</select>
</div>
<div>
<label for="edit_photo" class="block text-sm font-medium text-gray-700">Foto (Opsional)</label>
<input type="file" name="photo" id="edit_photo" accept="image/*"
class="mt-1 block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100">
<div id="current_photo" class="mt-2">
<!-- Current photo will be shown here -->
</div>
</div>
</div>
<div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<button type="submit"
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">
Simpan
</button>
<button type="button" onclick="closeEditModal()"
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:mt-0 sm:w-auto sm:text-sm">
Batal
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
@push('styles')
<link href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css" rel="stylesheet">
@endpush
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
loadSpecializations();
// Search functionality
document.getElementById('searchInput').addEventListener('input', function(e) {
const searchQuery = e.target.value.toLowerCase();
const cards = document.querySelectorAll('.specialization-card');
cards.forEach(card => {
const name = card.querySelector('.specialization-name').textContent.toLowerCase();
const category = card.querySelector('.specialization-category').textContent.toLowerCase();
if (name.includes(searchQuery) || category.includes(searchQuery)) {
card.style.display = '';
} else {
card.style.display = 'none';
}
});
});
});
async function loadSpecializations() {
try {
const response = await fetch(window.apiUrl('specializations'), {
headers: {
'Authorization': 'Bearer {{ session('api_token') }}',
'Accept': 'application/json'
}
});
if (!response.ok) {
throw new Error('Failed to fetch specializations');
}
const responseData = await response.json();
console.log('Loaded Specializations:', responseData);
const specializations = responseData.data;
// Group specializations by category
const groupedSpecializations = specializations.reduce((acc, spec) => {
if (!acc[spec.category]) {
acc[spec.category] = [];
}
acc[spec.category].push(spec);
return acc;
}, {});
// Render specializations
const container = document.getElementById('specializationsGrid');
container.innerHTML = Object.entries(groupedSpecializations).map(([category, specs]) => `
<div class="col-span-1 bg-white rounded-lg shadow-md overflow-hidden">
<div class="p-4 bg-gray-50 border-b">
<h3 class="text-lg font-semibold text-gray-800">${category}</h3>
</div>
<div class="p-4 space-y-4">
${specs.map(spec => `
<div class="specialization-card flex items-center justify-between p-3 bg-white border rounded-lg hover:shadow-md transition-shadow duration-200">
<div class="flex items-center space-x-4">
<div class="w-12 h-12 rounded-lg bg-gray-200 overflow-hidden">
${spec.photo ?
`<img src="${window.assetUrl(spec.photo)}"
alt="${spec.name}"
class="w-full h-full object-cover"
onerror="this.onerror=null; this.src='data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMTYgOEw1LjMzMzM0IDE2LjY2NjdMMTUuMzMzMyAxNi42NjY3TTE1LjMzMzMgMTYuNjY2N0wxNS4zMzMzIDQuNjY2NjdNMTUuMzMzMyAxNi42NjY3TDUuMzMzMzQgOC42NjY2NyIgc3Ryb2tlPSIjNjU3MThDIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjwvc3ZnPg=='">` :
`<div class="w-full h-full flex items-center justify-center bg-gray-300">
<svg class="w-6 h-6 text-gray-400" 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>
</div>`
}
</div>
<div>
<h4 class="specialization-name text-sm font-medium text-gray-900">${spec.name}</h4>
<p class="specialization-category text-sm text-gray-500">${spec.category}</p>
</div>
</div>
<div class="flex items-center space-x-2">
<button onclick="showEditModal(${spec.id})" class="text-gray-600 hover:text-gray-800" title="Edit">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"/>
</svg>
</button>
<button onclick="deleteSpecialization(${spec.id})" class="text-red-600 hover:text-red-800" title="Hapus">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="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"/>
</svg>
</button>
</div>
</div>
`).join('')}
</div>
</div>
`).join('');
} catch (error) {
console.error('Error:', error);
Swal.fire({
title: 'Error!',
text: 'Gagal memuat data spesialisasi',
icon: 'error',
confirmButtonText: 'OK',
confirmButtonColor: '#4F46E5'
});
}
}
function showAddModal() {
document.getElementById('addSpecializationModal').classList.remove('hidden');
}
function closeAddModal() {
document.getElementById('addSpecializationModal').classList.add('hidden');
document.getElementById('addSpecializationForm').reset();
}
async function addSpecialization(event) {
event.preventDefault();
const form = event.target;
const formData = new FormData(form);
try {
const response = await fetch(window.apiUrl('specializations'), {
method: 'POST',
headers: {
'Authorization': 'Bearer {{ session('api_token') }}',
'Accept': 'application/json'
},
body: formData
});
const responseData = await response.json();
console.log('API Response:', responseData); // Debug response
if (responseData.status === 'success') {
Swal.fire({
title: 'Berhasil!',
text: responseData.message,
icon: 'success',
confirmButtonText: 'OK',
confirmButtonColor: '#4F46E5'
}).then((result) => {
if (result.isConfirmed) {
closeAddModal();
loadSpecializations();
}
});
} else {
throw new Error(responseData.message || 'Gagal menambahkan spesialisasi');
}
} catch (error) {
console.error('Error:', error);
Swal.fire({
title: 'Gagal!',
text: error.message || 'Gagal menambahkan spesialisasi. Silakan coba lagi nanti.',
icon: 'error',
confirmButtonText: 'OK',
confirmButtonColor: '#4F46E5'
});
}
}
async function showEditModal(id) {
try {
const response = await fetch(window.apiUrl(`specializations/${id}`), {
headers: {
'Authorization': '{{ session('token_type') }} {{ session('api_token') }}',
'Accept': 'application/json'
}
});
if (!response.ok) {
throw new Error('Failed to fetch specialization data');
}
const data = await response.json();
const specialization = data.data;
// Fill form with specialization data
document.getElementById('edit_specialization_id').value = specialization.id;
document.getElementById('edit_name').value = specialization.name;
document.getElementById('edit_category').value = specialization.category;
// Show current photo if exists
const currentPhotoDiv = document.getElementById('current_photo');
if (specialization.photo) {
currentPhotoDiv.innerHTML = `
<img src="${window.assetUrl(specialization.photo)}"
alt="${specialization.name}"
class="h-20 w-20 object-cover rounded-lg">
`;
} else {
currentPhotoDiv.innerHTML = '';
}
// Show modal
document.getElementById('editSpecializationModal').classList.remove('hidden');
} catch (error) {
console.error('Error:', error);
Swal.fire({
title: 'Error!',
text: 'Gagal memuat data spesialisasi',
icon: 'error',
confirmButtonText: 'OK',
confirmButtonColor: '#4F46E5'
});
}
}
function closeEditModal() {
document.getElementById('editSpecializationModal').classList.add('hidden');
document.getElementById('editSpecializationForm').reset();
document.getElementById('current_photo').innerHTML = '';
}
async function updateSpecialization(event) {
event.preventDefault();
const form = event.target;
const formData = new FormData(form);
const id = document.getElementById('edit_specialization_id').value;
try {
const response = await fetch(window.apiUrl(`specializations/${id}/update`), {
method: 'POST',
headers: {
'Authorization': '{{ session('token_type') }} {{ session('api_token') }}',
'Accept': 'application/json'
},
body: formData
});
const responseData = await response.json();
console.log('Update Response:', responseData);
if (responseData.status === 'success' || responseData.message) {
Swal.fire({
title: 'Berhasil!',
text: responseData.message || 'Spesialisasi berhasil diperbarui',
icon: 'success',
confirmButtonText: 'OK',
confirmButtonColor: '#4F46E5'
}).then((result) => {
if (result.isConfirmed) {
closeEditModal();
loadSpecializations();
}
});
} else {
throw new Error(responseData.message || 'Gagal mengupdate spesialisasi');
}
} catch (error) {
console.error('Error:', error);
Swal.fire({
title: 'Gagal!',
text: error.message || 'Gagal mengupdate spesialisasi. Silakan coba lagi nanti.',
icon: 'error',
confirmButtonText: 'OK',
confirmButtonColor: '#4F46E5'
});
}
}
function deleteSpecialization(id) {
Swal.fire({
title: 'Anda yakin?',
text: "Spesialisasi ini akan dihapus dan tidak dapat dikembalikan!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Ya, hapus!',
cancelButtonText: 'Batal'
}).then(async (result) => {
if (result.isConfirmed) {
try {
const response = await fetch(window.apiUrl(`specializations/${id}`), {
method: 'DELETE',
headers: {
'Authorization': '{{ session('token_type') }} {{ session('api_token') }}',
'Accept': 'application/json'
}
});
const responseData = await response.json();
if (responseData.status === 'success') {
Swal.fire({
title: 'Berhasil!',
text: responseData.message,
icon: 'success',
confirmButtonText: 'OK',
confirmButtonColor: '#4F46E5'
}).then((result) => {
if (result.isConfirmed) {
loadSpecializations();
}
});
} else {
throw new Error(responseData.message || 'Gagal menghapus spesialisasi');
}
} catch (error) {
console.error('Error:', error);
Swal.fire({
title: 'Gagal!',
text: error.message || 'Gagal menghapus spesialisasi. Silakan coba lagi nanti.',
icon: 'error',
confirmButtonText: 'OK',
confirmButtonColor: '#4F46E5'
});
}
}
});
}
</script>
@endpush