This commit is contained in:
Arief Dwi Wicaksono 2025-09-11 10:28:30 +07:00
commit 55ae830e1d
2 changed files with 278 additions and 262 deletions

View File

@ -18,7 +18,7 @@
<table class="w-full table-fixed border border-gray-300 text-left"> <table class="w-full table-fixed border border-gray-300 text-left">
<thead class="bg-gray-100"> <thead class="bg-gray-100">
<tr> <tr>
<th class="w-[10%] p-2 border border-gray-300 text-center">Nomor</th> <th class="w-[7%] p-2 border border-gray-300 text-center">Nomor</th>
<th class="w-[70%] p-2 border border-gray-300 text-center">Fitur</th> <th class="w-[70%] p-2 border border-gray-300 text-center">Fitur</th>
<th class="w-[20%] p-2 border border-gray-300 text-center">Aksi</th> <th class="w-[20%] p-2 border border-gray-300 text-center">Aksi</th>
</tr> </tr>
@ -26,10 +26,10 @@
<tbody> <tbody>
@forelse ($fitur as $key => $item) @forelse ($fitur as $key => $item)
<tr class="hover:bg-gray-50"> <tr class="hover:bg-gray-50">
<td class="p-2 border border-gray-300">{{ $key + 1 }}</td> <td class="py-5 px-2 border border-gray-300 text-center">{{ $key + 1 }}</td>
<td class="p-2 border border-gray-300 truncate whitespace-nowrap">{{ $item->deskripsi }} <td class="py-5 px-2 border border-gray-300 truncate whitespace-nowrap">{{ $item->deskripsi }}
</td> </td>
<td class="p-2 border border-gray-300 text-center"> <td class="py-5 px-2 border border-gray-300 text-center">
<div class="flex justify-center space-x-2"> <div class="flex justify-center space-x-2">
<button class="text-blue-600 flex items-center pr-4 openEditModalBtn" <button class="text-blue-600 flex items-center pr-4 openEditModalBtn"
data-id="{{ $item->id }}"> data-id="{{ $item->id }}">

View File

@ -3,277 +3,293 @@
@section('title', 'Manajemen Template') @section('title', 'Manajemen Template')
@section('content') @section('content')
<div class="container mx-auto py-4"> <div class="container mx-auto py-4">
<!-- Header --> <!-- Header -->
<div class="flex justify-between items-center mb-4"> <div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-bold"> <h3 class="text-xl font-bold">
@isset($kategori) @isset($kategori)
Template Kategori: {{ $kategori->nama }} Template Kategori: {{ $kategori->nama }}
@else @else
Semua Template Semua Template
@endisset @endisset
</h3> </h3>
@if (!isset($kategori)) @if (!isset($kategori))
<button id="openTambahModal" class="bg-blue-600 text-white px-3 py-2.5 rounded flex items-center"> <button id="openTambahModal" class="bg-blue-600 text-white px-3 py-2.5 rounded flex items-center">
<i class="bi bi-plus-lg mr-1"></i> Tambah Template <i class="bi bi-plus-lg mr-1"></i> Tambah Template
</button> </button>
@endif
</div>
<!-- Flash Message -->
@if (session('success'))
<div id="toast-success" class="mb-4 p-3 rounded bg-green-100 text-green-800 border border-green-300 shadow">
{{ session('success') }}
</div>
<script>
setTimeout(() => document.getElementById('toast-success')?.remove(), 3000);
</script>
@endif @endif
</div>
<!-- Flash Message --> @if ($errors->any())
@if (session('success')) <div id="toast-error" class="mb-4 p-3 rounded bg-red-100 text-red-800 border border-red-300 shadow">
<div id="toast-success" class="mb-4 p-3 rounded bg-green-100 text-green-800 border border-green-300 shadow"> <ul class="list-disc ml-5">
{{ session('success') }} @foreach ($errors->all() as $error)
</div> <li>{{ $error }}</li>
<script>setTimeout(() => document.getElementById('toast-success')?.remove(), 3000);</script> @endforeach
@endif </ul>
@if ($errors->any())
<div id="toast-error" class="mb-4 p-3 rounded bg-red-100 text-red-800 border border-red-300 shadow">
<ul class="list-disc ml-5">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
<script>setTimeout(() => document.getElementById('toast-error')?.remove(), 5000);</script>
@endif
<!-- Tabel Template -->
<div class="bg-white rounded-lg shadow-sm">
<div class="p-4 overflow-x-auto">
<table class="w-full table-fixed text-left border border-gray-300 border-collapse">
<thead class="bg-gray-100">
<tr>
<th class="p-2 border border-gray-300 w-[50px] text-center">Nomor</th>
<th class="p-2 border border-gray-300 w-[200px] text-center">Nama Template</th>
<th class="p-2 border border-gray-300 w-[150px] text-center">Kategori</th>
<th class="p-2 border border-gray-300 w-[150px] text-center">Fitur</th>
<th class="p-2 border border-gray-300 w-[90px] text-center">Foto</th>
<th class="p-2 border border-gray-300 w-[100px] text-center">Harga</th>
<th class="p-2 border border-gray-300 w-[130px] text-center">Aksi</th>
</tr>
</thead>
<tbody>
@forelse($templates as $key => $template)
<tr>
<td class="p-2 border border-gray-300 text-center">{{ $key + 1 }}</td>
<td class="p-2 border border-gray-300 truncate">{{ $template->nama_template }}</td>
<td class="p-2 border border-gray-300 truncate">{{ $template->kategori->nama ?? '-' }}</td>
<td class="p-2 border border-gray-300 truncate">{{ $template->fitur->deskripsi ?? '-' }}</td>
<td class="p-2 border border-gray-300 text-center">
<div class="w-12 h-12 overflow-hidden rounded bg-gray-100 flex items-center justify-center mx-auto">
<img src="{{ $template->foto ? asset('storage/' . $template->foto) : asset('default-image.png') }}"
alt="foto" class="max-w-full max-h-full object-contain">
</div>
</td>
<td class="p-2 border border-gray-300 text-right font-semibold">
Rp {{ number_format($template->harga, 0, ',', '.') }}
</td>
<td class="p-2 border border-gray-300 text-center">
<div class="flex justify-center space-x-2">
<button class="text-blue-600 hover:underline flex items-center openEditModalBtn"
data-id="{{ $template->id }}">
<i class="bi bi-pencil mr-1"></i> Edit
</button>
<form action="{{ route('templates.destroy', $template->id) }}" method="POST"
class="inline" onsubmit="return confirm('Hapus template ini?')">
@csrf
@method('DELETE')
<button class="text-red-600 hover:underline flex items-center">
<i class="bi bi-trash mr-1"></i> Hapus
</button>
</form>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="7" class="p-2 text-center text-gray-500 border border-gray-300">Belum ada template</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
<!-- Modal Tambah Template -->
@if (!isset($kategori))
<div id="modalTambah" class="fixed inset-0 hidden items-center justify-center z-50">
<div class="absolute inset-0 bg-black opacity-50" id="closeTambahModal"></div>
<div class="bg-white rounded-lg shadow-lg w-full max-w-md z-50 overflow-hidden">
<form action="{{ route('templates.store') }}" method="POST" enctype="multipart/form-data">
@csrf
<div class="p-4 border-b">
<h5 class="text-lg font-medium">Tambah Template</h5>
</div>
<div class="p-4 space-y-4">
<div>
<label class="block text-sm font-medium">Nama Template</label>
<input type="text" name="nama_template" class="w-full p-2 border rounded" required>
</div>
<div>
<label class="block text-sm font-medium">Kategori</label>
<select name="kategori_id" class="w-full p-2 border rounded" required>
@foreach ($kategoris as $kategori)
<option value="{{ $kategori->id }}">{{ $kategori->nama }}</option>
@endforeach
</select>
</div>
<div>
<label class="block text-sm font-medium">Fitur</label>
<select name="fitur_id" class="w-full p-2 border rounded" required>
@foreach ($fiturs as $fitur)
<option value="{{ $fitur->id }}">{{ $fitur->deskripsi }}</option>
@endforeach
</select>
</div>
<div>
<label class="block text-sm font-medium">Harga</label>
<input type="number" name="harga" class="w-full p-2 border rounded" required min="0" step="1000">
</div>
<div>
<label class="block text-sm font-medium">Foto (opsional)</label>
<input type="file" name="foto" class="w-full p-2 border rounded" accept="image/*">
<small class="text-gray-500">Format yang didukung: JPG, PNG, GIF. Maksimal 5MB.</small>
</div>
</div>
<div class="p-4 border-t flex justify-end space-x-2">
<button type="button" id="closeTambahBtn" class="bg-gray-300 text-black px-3 py-1 rounded">Batal</button>
<button class="bg-blue-600 text-white px-3 py-1 rounded">Simpan</button>
</div>
</form>
</div> </div>
</div> <script>
@endif setTimeout(() => document.getElementById('toast-error')?.remove(), 5000);
</script>
@endif
<!-- Modal Edit Template --> <!-- Tabel Template -->
@foreach ($templates as $template) <div class="bg-white rounded-lg shadow-sm">
<div id="modalEdit{{ $template->id }}" class="fixed inset-0 hidden items-center justify-center z-50"> <div class="p-4 overflow-x-auto">
<div class="absolute inset-0 bg-black opacity-50 closeEditOverlay" data-id="{{ $template->id }}"></div> <table class="w-full table-fixed text-left border border-gray-300 border-collapse">
<div class="bg-white rounded-lg shadow-lg w-full max-w-md z-50 overflow-hidden"> <thead class="bg-gray-100">
<form action="{{ route('templates.update', $template->id) }}" method="POST" enctype="multipart/form-data"> <tr>
@csrf @method('PUT') <th class="p-2 border border-gray-300 w-[50px] text-center">Nomor</th>
<div class="p-4 border-b"> <th class="p-2 border border-gray-300 w-[200px] text-center">Nama Template</th>
<h5 class="text-lg font-medium">Edit Template</h5> <th class="p-2 border border-gray-300 w-[150px] text-center">Kategori</th>
</div> <th class="p-2 border border-gray-300 w-[150px] text-center">Fitur</th>
<div class="p-4 space-y-4"> <th class="p-2 border border-gray-300 w-[90px] text-center">Foto</th>
<div> <th class="p-2 border border-gray-300 w-[100px] text-center">Harga</th>
<label class="block text-sm font-medium">Nama Template</label> <th class="p-2 border border-gray-300 w-[130px] text-center">Aksi</th>
<input type="text" name="nama_template" value="{{ $template->nama_template }}" </tr>
class="w-full p-2 border rounded" required> </thead>
</div> <tbody>
<div> @forelse($templates as $key => $template)
<label class="block text-sm font-medium">Kategori</label> <tr>
<select name="kategori_id" class="w-full p-2 border rounded" required> <td class="p-2 border border-gray-300 text-center">{{ $key + 1 }}</td>
@foreach ($kategoris as $kategori) <td class="p-2 border border-gray-300 truncate">{{ $template->nama_template }}</td>
<option value="{{ $kategori->id }}" @selected($kategori->id == $template->kategori_id)>{{ $kategori->nama }}</option> <td class="p-2 border border-gray-300 truncate">{{ $template->kategori->nama ?? '-' }}</td>
@endforeach <td class="p-2 border border-gray-300 truncate">{{ $template->fitur->deskripsi ?? '-' }}
</select> </td>
</div> <td class="p-2 border border-gray-300 text-center">
<div> <div
<label class="block text-sm font-medium">Fitur</label> class="w-12 h-12 overflow-hidden rounded bg-gray-100 flex items-center justify-center mx-auto">
<select name="fitur_id" class="w-full p-2 border rounded" required> <img src="{{ $template->foto ? asset('storage/' . $template->foto) : asset('default-image.png') }}"
@foreach ($fiturs as $fitur) alt="foto" class="max-w-full max-h-full object-contain">
<option value="{{ $fitur->id }}" @selected($fitur->id == $template->fitur_id)>{{ $fitur->deskripsi }}</option>
@endforeach
</select>
</div>
<div>
<label class="block text-sm font-medium">Harga</label>
<input type="number" name="harga" value="{{ $template->harga }}"
class="w-full p-2 border rounded" required min="0" step="1000">
</div>
<div>
<label class="block text-sm font-medium">Foto (opsional)</label>
@if ($template->foto)
<div class="mb-2">
<small class="text-gray-500">Foto saat ini:</small>
<div class="w-20 h-20 mt-1 overflow-hidden rounded bg-gray-100 flex items-center justify-center border">
<img src="{{ asset('storage/' . $template->foto) }}" alt="foto"
class="max-w-full max-h-full object-contain">
</div> </div>
</div> </td>
@endif <td class="p-2 border border-gray-300 text-left font-semibold">
<input type="file" name="foto" class="w-full p-2 border rounded" accept="image/*"> Rp {{ number_format($template->harga, 0, ',', '.') }}
</div> </td>
</div> <td class="p-2 border border-gray-300 text-center">
<div class="p-4 border-t flex justify-end space-x-2"> <div class="flex justify-center space-x-2">
<button type="button" class="bg-gray-300 text-black px-3 py-1 rounded closeEditBtn" <button class="text-blue-600 hover:underline flex items-center openEditModalBtn"
data-id="{{ $template->id }}">Batal</button> data-id="{{ $template->id }}">
<button class="bg-blue-600 text-white px-3 py-1 rounded">Simpan Perubahan</button> <i class="bi bi-pencil mr-1"></i> Edit
</div> </button>
</form> <form action="{{ route('templates.destroy', $template->id) }}" method="POST"
class="inline" onsubmit="return confirm('Hapus template ini?')">
@csrf
@method('DELETE')
<button class="text-red-600 hover:underline flex items-center">
<i class="bi bi-trash mr-1"></i> Hapus
</button>
</form>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="7" class="p-2 text-center text-gray-500 border border-gray-300">Belum ada
template</td>
</tr>
@endforelse
</tbody>
</table>
</div> </div>
</div> </div>
@endforeach
<script> <!-- Modal Tambah Template -->
document.addEventListener('DOMContentLoaded', () => { @if (!isset($kategori))
// Modal Tambah <div id="modalTambah" class="fixed inset-0 hidden items-center justify-center z-50">
const openTambahModal = document.getElementById('openTambahModal'); <div class="absolute inset-0 bg-black opacity-50" id="closeTambahModal"></div>
const modalTambah = document.getElementById('modalTambah'); <div class="bg-white rounded-lg shadow-lg w-full max-w-md z-50 overflow-hidden">
const closeTambahBtn = document.getElementById('closeTambahBtn'); <form action="{{ route('templates.store') }}" method="POST" enctype="multipart/form-data">
const closeTambahOverlay = document.getElementById('closeTambahModal'); @csrf
<div class="p-4 border-b">
<h5 class="text-lg font-medium">Tambah Template</h5>
</div>
<div class="p-4 space-y-4">
<div>
<label class="block text-sm font-medium">Nama Template</label>
<input type="text" name="nama_template" class="w-full p-2 border rounded" required>
</div>
<div>
<label class="block text-sm font-medium">Kategori</label>
<select name="kategori_id" class="w-full p-2 border rounded" required>
@foreach ($kategoris as $kategori)
<option value="{{ $kategori->id }}">{{ $kategori->nama }}</option>
@endforeach
</select>
</div>
<div>
<label class="block text-sm font-medium">Fitur</label>
<select name="fitur_id" class="w-full p-2 border rounded" required>
@foreach ($fiturs as $fitur)
<option value="{{ $fitur->id }}">
{{ \Illuminate\Support\Str::limit($fitur->deskripsi, 50) }}
</option>
@endforeach
</select>
if (openTambahModal) { </div>
openTambahModal.addEventListener('click', () => { <div>
modalTambah.classList.remove('hidden'); <label class="block text-sm font-medium">Harga</label>
modalTambah.classList.add('flex'); <input type="number" name="harga" class="w-full p-2 border rounded" required
min="0" step="1000">
</div>
<div>
<label class="block text-sm font-medium">Foto (opsional)</label>
<input type="file" name="foto" class="w-full p-2 border rounded" accept="image/*">
<small class="text-gray-500">Format yang didukung: JPG, PNG, GIF. Maksimal 5MB.</small>
</div>
</div>
<div class="p-4 border-t flex justify-end space-x-2">
<button type="button" id="closeTambahBtn"
class="bg-gray-300 text-black px-3 py-1 rounded">Batal</button>
<button class="bg-blue-600 text-white px-3 py-1 rounded">Simpan</button>
</div>
</form>
</div>
</div>
@endif
<!-- Modal Edit Template -->
@foreach ($templates as $template)
<div id="modalEdit{{ $template->id }}" class="fixed inset-0 hidden items-center justify-center z-50">
<div class="absolute inset-0 bg-black opacity-50 closeEditOverlay" data-id="{{ $template->id }}"></div>
<div class="bg-white rounded-lg shadow-lg w-full max-w-md z-50 overflow-hidden">
<form action="{{ route('templates.update', $template->id) }}" method="POST"
enctype="multipart/form-data">
@csrf @method('PUT')
<div class="p-4 border-b">
<h5 class="text-lg font-medium">Edit Template</h5>
</div>
<div class="p-4 space-y-4">
<div>
<label class="block text-sm font-medium">Nama Template</label>
<input type="text" name="nama_template" value="{{ $template->nama_template }}"
class="w-full p-2 border rounded" required>
</div>
<div>
<label class="block text-sm font-medium">Kategori</label>
<select name="kategori_id" class="w-full p-2 border rounded" required>
@foreach ($kategoris as $kategori)
<option value="{{ $kategori->id }}" @selected($kategori->id == $template->kategori_id)>
{{ $kategori->nama }}</option>
@endforeach
</select>
</div>
<div>
<label class="block text-sm font-medium">Fitur</label>
<select name="fitur_id" class="w-full p-2 border rounded" required>
@foreach ($fiturs as $fitur)
<option value="{{ $fitur->id }}" @selected($fitur->id == $template->fitur_id)>
{{ $fitur->deskripsi }}</option>
@endforeach
</select>
</div>
<div>
<label class="block text-sm font-medium">Harga</label>
<input type="number" name="harga" value="{{ $template->harga }}"
class="w-full p-2 border rounded" required min="0" step="1000">
</div>
<div>
<label class="block text-sm font-medium">Foto (opsional)</label>
@if ($template->foto)
<div class="mb-2">
<small class="text-gray-500">Foto saat ini:</small>
<div
class="w-20 h-20 mt-1 overflow-hidden rounded bg-gray-100 flex items-center justify-center border">
<img src="{{ asset('storage/' . $template->foto) }}" alt="foto"
class="max-w-full max-h-full object-contain">
</div>
</div>
@endif
<input type="file" name="foto" class="w-full p-2 border rounded" accept="image/*">
</div>
</div>
<div class="p-4 border-t flex justify-end space-x-2">
<button type="button" class="bg-gray-300 text-black px-3 py-1 rounded closeEditBtn"
data-id="{{ $template->id }}">Batal</button>
<button class="bg-blue-600 text-white px-3 py-1 rounded">Simpan Perubahan</button>
</div>
</form>
</div>
</div>
@endforeach
<script>
document.addEventListener('DOMContentLoaded', () => {
// Modal Tambah
const openTambahModal = document.getElementById('openTambahModal');
const modalTambah = document.getElementById('modalTambah');
const closeTambahBtn = document.getElementById('closeTambahBtn');
const closeTambahOverlay = document.getElementById('closeTambahModal');
if (openTambahModal) {
openTambahModal.addEventListener('click', () => {
modalTambah.classList.remove('hidden');
modalTambah.classList.add('flex');
});
}
if (closeTambahBtn) {
closeTambahBtn.addEventListener('click', () => {
modalTambah.classList.add('hidden');
modalTambah.classList.remove('flex');
});
}
if (closeTambahOverlay) {
closeTambahOverlay.addEventListener('click', () => {
modalTambah.classList.add('hidden');
modalTambah.classList.remove('flex');
});
}
// Modal Edit
const openEditBtns = document.querySelectorAll('.openEditModalBtn');
const closeEditBtns = document.querySelectorAll('.closeEditBtn');
const closeEditOverlays = document.querySelectorAll('.closeEditOverlay');
openEditBtns.forEach(btn => {
btn.addEventListener('click', () => {
const id = btn.dataset.id;
const modal = document.getElementById('modalEdit' + id);
if (modal) {
modal.classList.remove('hidden');
modal.classList.add('flex');
}
});
}); });
}
if (closeTambahBtn) {
closeTambahBtn.addEventListener('click', () => {
modalTambah.classList.add('hidden');
modalTambah.classList.remove('flex');
});
}
if (closeTambahOverlay) {
closeTambahOverlay.addEventListener('click', () => {
modalTambah.classList.add('hidden');
modalTambah.classList.remove('flex');
});
}
// Modal Edit closeEditBtns.forEach(btn => {
const openEditBtns = document.querySelectorAll('.openEditModalBtn'); btn.addEventListener('click', () => {
const closeEditBtns = document.querySelectorAll('.closeEditBtn'); const id = btn.dataset.id;
const closeEditOverlays = document.querySelectorAll('.closeEditOverlay'); const modal = document.getElementById('modalEdit' + id);
if (modal) {
modal.classList.add('hidden');
modal.classList.remove('flex');
}
});
});
openEditBtns.forEach(btn => { closeEditOverlays.forEach(overlay => {
btn.addEventListener('click', () => { overlay.addEventListener('click', () => {
const id = btn.dataset.id; const id = overlay.dataset.id;
const modal = document.getElementById('modalEdit' + id); const modal = document.getElementById('modalEdit' + id);
if (modal) { if (modal) {
modal.classList.remove('hidden'); modal.classList.add('hidden');
modal.classList.add('flex'); modal.classList.remove('flex');
} }
});
}); });
}); });
</script>
closeEditBtns.forEach(btn => { </div>
btn.addEventListener('click', () => {
const id = btn.dataset.id;
const modal = document.getElementById('modalEdit' + id);
if (modal) {
modal.classList.add('hidden');
modal.classList.remove('flex');
}
});
});
closeEditOverlays.forEach(overlay => {
overlay.addEventListener('click', () => {
const id = overlay.dataset.id;
const modal = document.getElementById('modalEdit' + id);
if (modal) {
modal.classList.add('hidden');
modal.classList.remove('flex');
}
});
});
});
</script>
</div>
@endsection @endsection