497 lines
24 KiB
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
|