fix radio & checkbox

This commit is contained in:
Muzakki Parsaoran Siregar 2025-09-18 11:38:26 +07:00
parent e322f2fa37
commit 8a02c512c2
6 changed files with 387 additions and 239 deletions

View File

@ -1,30 +1,27 @@
<?php <?php
// app/Http/Controllers/FiturController.php
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\Fitur; use App\Models\Fitur;
use Illuminate\Http\Request;
use App\Models\KategoriFitur; use App\Models\KategoriFitur;
use Illuminate\Http\Request;
class FiturController extends Controller class FiturController extends Controller
{ {
// Tampilkan semua fitur public function index()
public function index() {
{ $fitur = Fitur::with('kategoriFitur')->paginate(5);
$fitur = Fitur::with('kategoriFitur')->paginate(5); $kategoriFiturs = KategoriFitur::all();
$kategoriFiturs = KategoriFitur::all();
return view('admin.fitur.index', compact('fitur', 'kategoriFiturs')); return view('admin.fitur.index', compact('fitur', 'kategoriFiturs'));
} }
// Simpan fitur baru
public function store(Request $request) public function store(Request $request)
{ {
$request->validate([ $request->validate([
'deskripsi' => 'required|string|max:255', 'deskripsi' => 'required|string|max:255',
'harga' => 'required|numeric|min:0', 'harga' => 'required|numeric|min:0',
'kategori_fitur_id' => 'required|exists:kategori_fitur,id', 'kategori_fitur_id' => 'nullable|exists:kategori_fitur,id',
]); ]);
Fitur::create([ Fitur::create([
@ -33,32 +30,35 @@ public function index()
'kategori_fitur_id' => $request->kategori_fitur_id, 'kategori_fitur_id' => $request->kategori_fitur_id,
]); ]);
return redirect()->route('admin.fitur.index')->with('success', 'Fitur berhasil ditambahkan'); return redirect()->route('admin.fitur.index')
->with('success', 'Fitur berhasil ditambahkan');
} }
// Update fitur
public function update(Request $request, $id) public function update(Request $request, $id)
{ {
$request->validate([ $request->validate([
'deskripsi' => 'required|string|max:255', 'deskripsi' => 'required|string|max:255',
'harga' => 'required|numeric|min:0', 'harga' => 'required|numeric|min:0',
'kategori_fitur_id' => 'nullable|exists:kategori_fitur,id',
]); ]);
$fitur = Fitur::findOrFail($id); $fitur = Fitur::findOrFail($id);
$fitur->update([ $fitur->update([
'deskripsi' => $request->deskripsi, 'deskripsi' => $request->deskripsi,
'harga' => $request->harga, 'harga' => $request->harga,
'kategori_fitur_id' => $request->kategori_fitur_id,
]); ]);
return redirect()->route('admin.fitur.index')->with('success', 'Fitur berhasil diperbarui'); return redirect()->route('admin.fitur.index')
->with('success', 'Fitur berhasil diperbarui');
} }
// Hapus fitur
public function destroy($id) public function destroy($id)
{ {
$fitur = Fitur::findOrFail($id); $fitur = Fitur::findOrFail($id);
$fitur->delete(); $fitur->delete();
return redirect()->route('admin.fitur.index')->with('success', 'Fitur berhasil dihapus'); return redirect()->route('admin.fitur.index')
->with('success', 'Fitur berhasil dihapus');
} }
} }

View File

@ -5,20 +5,35 @@ namespace App\Http\Controllers;
use App\Models\Template; use App\Models\Template;
use App\Models\Kategori; use App\Models\Kategori;
use App\Models\Fitur; use App\Models\Fitur;
use App\Models\KategoriFitur; // <-- tambah ini
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
class TemplateController extends Controller class TemplateController extends Controller
{ {
public function index() public function index()
{ {
// eager load fiturs & kategori $templates = Template::with(['kategori', 'fiturs'])->paginate(5);
$templates = Template::with(['kategori', 'fiturs'])->paginate(5); $kategoris = Kategori::all();
$kategoris = Kategori::all();
$fiturs = Fitur::all(); // semua fitur dengan relasi kategori
$fiturs = Fitur::with('kategoriFitur')->get();
// kategori fitur beserta fiturnya
$kategoriFiturs = KategoriFitur::with('fiturs')->get();
// fitur yang tidak punya kategori
$fitursTanpaKategori = Fitur::whereNull('kategori_fitur_id')->get();
return view('admin.templates.index', compact(
'templates',
'kategoris',
'fiturs',
'kategoriFiturs',
'fitursTanpaKategori'
));
}
return view('admin.templates.index', compact('templates', 'kategoris', 'fiturs'));
}
public function show($id) public function show($id)
{ {
@ -108,15 +123,25 @@ class TemplateController extends Controller
return redirect()->route('templates.index')->with('success', 'Template berhasil dihapus!'); return redirect()->route('templates.index')->with('success', 'Template berhasil dihapus!');
} }
public function byKategori($id) public function byKategori($id)
{ {
$kategori = Kategori::findOrFail($id); $kategori = Kategori::findOrFail($id);
$templates = Template::with(['kategori', 'fiturs']) $templates = Template::with(['kategori', 'fiturs'])
->where('kategori_id', $id) ->where('kategori_id', $id)
->paginate(5); ->paginate(5);
$kategoris = Kategori::all(); $kategoris = Kategori::all();
$fiturs = Fitur::all(); $fiturs = Fitur::with('kategoriFitur')->get();
$kategoriFiturs = KategoriFitur::with('fiturs')->get();
$fitursTanpaKategori = Fitur::whereNull('kategori_fitur_id')->get(); // <-- tambahkan ini
return view('admin.templates.index', compact(
'templates',
'kategoris',
'fiturs',
'kategori',
'kategoriFiturs',
'fitursTanpaKategori' // <-- kirim ke view
));
}
return view('admin.templates.index', compact('templates', 'kategoris', 'fiturs', 'kategori'));
}
} }

View File

@ -18,9 +18,9 @@ class Fitur extends Model
]; ];
public function kategoriFitur() public function kategoriFitur()
{ {
return $this->belongsTo(KategoriFitur::class, 'kategori_fitur_id'); return $this->belongsTo(KategoriFitur::class, 'kategori_fitur_id');
} }
} }

View File

@ -14,4 +14,10 @@ class KategoriFitur extends Model
protected $fillable = [ protected $fillable = [
'deskripsi', 'deskripsi',
]; ];
// relasi: satu kategori punya banyak fitur
public function fiturs()
{
return $this->hasMany(Fitur::class, 'kategori_fitur_id');
}
} }

View File

@ -157,30 +157,43 @@
<h5 class="text-lg font-medium">Tambah Fitur</h5> <h5 class="text-lg font-medium">Tambah Fitur</h5>
</div> </div>
<div class="p-4 space-y-4"> <div class="p-4 space-y-4">
<!-- Kategori -->
<div> {{-- Kategori --}}
<label class="block text-sm font-medium">Kategori Fitur</label> @if($kategori_fitur ?? false)
<select name="kategori_fitur_id" class="w-full p-2 border rounded" required> <!-- Jika controller mengirim $kategori_fitur -->
<option value="">-- Pilih Kategori --</option> <input type="hidden" name="kategori_fitur_id" value="{{ optional($kategori_fitur ?? null)->id }}">
@foreach(\App\Models\KategoriFitur::all() as $kategori) <p class="text-sm text-gray-600">
<option value="{{ $kategori->id }}">{{ $kategori->deskripsi }}</option> Kategori: <strong>{{ optional($kategori_fitur ?? null)->deskripsi }}</strong>
@endforeach </p>
</select> @else
</div> <!-- Kalau tidak ada kategori tertentu, tampilkan dropdown -->
<!-- Nama Fitur --> <div>
<label class="block text-sm font-medium">Kategori Fitur</label>
<select name="kategori_fitur_id" class="w-full p-2 border rounded">
<option value="">-- Pilih Kategori --</option>
@foreach(\App\Models\KategoriFitur::all() as $kategori)
<option value="{{ $kategori->id }}">{{ $kategori->deskripsi }}</option>
@endforeach
</select>
</div>
@endif
{{-- Nama Fitur --}}
<div> <div>
<label class="block text-sm font-medium">Nama Fitur</label> <label class="block text-sm font-medium">Nama Fitur</label>
<input type="text" name="deskripsi" class="w-full p-2 border rounded" required> <input type="text" name="deskripsi" class="w-full p-2 border rounded" required>
</div> </div>
<!-- Harga -->
{{-- Harga --}}
<div> <div>
<label class="block text-sm font-medium">Harga</label> <label class="block text-sm font-medium">Harga</label>
<input type="number" name="harga" step="100" min="0" class="w-full p-2 border rounded" required> <input type="number" name="harga" step="100" min="0" class="w-full p-2 border rounded" required>
</div> </div>
</div> </div>
<div class="p-4 border-t flex justify-end space-x-2"> <div class="p-4 border-t flex justify-end space-x-2">
<button type="button" id="closeTambahBtn" <button type="button" id="closeTambahBtn"
class="bg-gray-300 text-black px-3 py-1 rounded">Batal</button> 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> <button class="bg-blue-600 text-white px-3 py-1 rounded">Simpan</button>
</div> </div>
</form> </form>
@ -199,35 +212,48 @@
<h5 class="text-lg font-medium">Edit Fitur</h5> <h5 class="text-lg font-medium">Edit Fitur</h5>
</div> </div>
<div class="p-4 space-y-4"> <div class="p-4 space-y-4">
<!-- Kategori -->
<div> {{-- Kategori --}}
<label class="block text-sm font-medium">Kategori Fitur</label> @if($kategori_fitur ?? false)
<select name="kategori_fitur_id" class="w-full p-2 border rounded" required> <!-- Jika controller mengirim $kategori_fitur -->
<option value="">-- Pilih Kategori --</option> <input type="hidden" name="kategori_fitur_id" value="{{ optional($kategori_fitur ?? null)->id }}">
@foreach(\App\Models\KategoriFitur::all() as $kategori) <p class="text-sm text-gray-600">
<option value="{{ $kategori->id }}" Kategori: <strong>{{ optional($kategori_fitur ?? null)->deskripsi }}</strong>
{{ $item->kategori_fitur_id == $kategori->id ? 'selected' : '' }}> </p>
{{ $kategori->deskripsi }} @else
</option> <!-- Kalau tidak ada kategori tertentu, tampilkan dropdown -->
@endforeach <div>
</select> <label class="block text-sm font-medium">Kategori Fitur</label>
</div> <select name="kategori_fitur_id" class="w-full p-2 border rounded">
<!-- Nama Fitur --> <option value="">-- Pilih Kategori --</option>
@foreach(\App\Models\KategoriFitur::all() as $kategori)
<option value="{{ $kategori->id }}"
{{ $item->kategori_fitur_id == $kategori->id ? 'selected' : '' }}>
{{ $kategori->deskripsi }}
</option>
@endforeach
</select>
</div>
@endif
{{-- Nama Fitur --}}
<div> <div>
<label class="block text-sm font-medium">Nama Fitur</label> <label class="block text-sm font-medium">Nama Fitur</label>
<input type="text" name="deskripsi" value="{{ $item->deskripsi }}" <input type="text" name="deskripsi" value="{{ $item->deskripsi }}"
class="w-full p-2 border rounded" required> class="w-full p-2 border rounded" required>
</div> </div>
<!-- Harga -->
{{-- Harga --}}
<div> <div>
<label class="block text-sm font-medium">Harga</label> <label class="block text-sm font-medium">Harga</label>
<input type="number" name="harga" value="{{ $item->harga }}" <input type="number" name="harga" value="{{ $item->harga }}"
step="100" min="0" class="w-full p-2 border rounded" required> step="100" min="0" class="w-full p-2 border rounded" required>
</div> </div>
</div> </div>
<div class="p-4 border-t flex justify-end space-x-2"> <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" <button type="button" class="bg-gray-300 text-black px-3 py-1 rounded closeEditBtn"
data-id="{{ $item->id }}">Batal</button> data-id="{{ $item->id }}">Batal</button>
<button class="bg-blue-600 text-white px-3 py-1 rounded">Simpan Perubahan</button> <button class="bg-blue-600 text-white px-3 py-1 rounded">Simpan Perubahan</button>
</div> </div>
</form> </form>
@ -236,6 +262,7 @@
@endforeach @endforeach
<!-- Modal Hapus --> <!-- Modal Hapus -->
@foreach ($fitur as $item) @foreach ($fitur as $item)
<div id="modalDelete{{ $item->id }}" class="fixed inset-0 hidden items-center justify-center z-50"> <div id="modalDelete{{ $item->id }}" class="fixed inset-0 hidden items-center justify-center z-50">

View File

@ -204,150 +204,223 @@
@endif @endif
<!-- Modal Tambah Template --> <!-- Modal Tambah Template -->
@if (!isset($kategori)) @if (!isset($kategori))
<div id="modalTambah" class="fixed inset-0 hidden items-center justify-center z-50"> <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="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"> <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"> <form action="{{ route('templates.store') }}" method="POST" enctype="multipart/form-data">
@csrf @csrf
<div class="p-4 border-b"> <div class="p-4 border-b">
<h5 class="text-lg font-medium">Tambah Template</h5> <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
value="{{ old('nama_template') }}">
</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 $kategoriItem)
<option value="{{ $kategoriItem->id }}" @selected(old('kategori_id') == $kategoriItem->id)>
{{ $kategoriItem->nama }}</option>
@endforeach
</select>
</div>
<!-- CHECKBOX FITUR -->
<div>
<label class="block text-sm font-medium">Fitur</label>
<div
class="grid grid-cols-1 sm:grid-cols-2 gap-2 max-h-48 overflow-auto p-2 border rounded">
@foreach ($fiturs as $fitur)
<label class="flex items-center space-x-2">
<input type="checkbox" name="fitur_id[]" value="{{ $fitur->id }}"
data-harga="{{ $fitur->harga }}"
{{ is_array(old('fitur_id')) && in_array($fitur->id, old('fitur_id')) ? 'checked' : '' }}>
<span class="text-sm">
{{ \Illuminate\Support\Str::limit($fitur->deskripsi, 80) }}
(Rp {{ number_format($fitur->harga,0,',','.') }})
</span>
</label>
@endforeach
</div>
</div>
<div>
<label class="block text-sm font-medium">Harga</label>
<input type="text" name="harga" class="w-full p-2 border rounded" required
min="0" value="{{ old('harga') }}">
</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: JPG, PNG, GIF. Maks 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> <div class="p-4 space-y-4">
@endif <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
value="{{ old('nama_template') }}">
</div>
<div>
<label class="block text-sm font-medium">Kategori</label>
<select name="kategori_id" id="kategoriSelectTambah" class="w-full p-2 border rounded">
<option value="">-- Pilih Kategori --</option>
@foreach ($kategoris as $kategoriItem)
<option value="{{ $kategoriItem->id }}" @selected(old('kategori_id') == $kategoriItem->id)>
{{ $kategoriItem->nama }}
</option>
@endforeach
</select>
</div>
<!-- Modal Edit Template --> <!-- FITUR -->
@foreach ($templates as $template) <div>
<div id="modalEdit{{ $template->id }}" class="fixed inset-0 hidden items-center justify-center z-50"> <label class="block text-sm font-medium">Fitur</label>
<div class="absolute inset-0 bg-black opacity-50 closeEditOverlay" data-id="{{ $template->id }}"></div> <div id="fiturContainerTambah"
<div class="bg-white rounded-lg shadow-lg w-full max-w-md z-50 overflow-hidden"> class="space-y-3 max-h-64 overflow-auto p-2 border rounded">
<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="{{ old('nama_template', $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 $kategoriItem)
<option value="{{ $kategoriItem->id }}" @selected($kategoriItem->id == old('kategori_id', $template->kategori_id))>
{{ $kategoriItem->nama }}</option>
@endforeach
</select>
</div>
<!-- EDIT: checkbox fitur dengan checked state --> {{-- Fitur dengan kategori radio --}}
<div> @foreach ($kategoriFiturs as $kategoriFitur)
<label class="block text-sm font-medium">Fitur</label> <div>
<div <p class="font-semibold text-gray-700 mb-1">
class="grid grid-cols-1 sm:grid-cols-2 gap-2 max-h-48 overflow-auto p-2 border rounded"> {{ $kategoriFitur->deskripsi }}
@foreach ($fiturs as $fiturItem) </p>
<label class="flex items-center space-x-2"> <div class="grid grid-cols-1 sm:grid-cols-2 gap-2">
<input type="checkbox" name="fitur_id[]" value="{{ $fiturItem->id }}" @foreach ($fiturs->where('kategori_fitur_id', $kategoriFitur->id) as $fitur)
data-harga="{{ $fiturItem->harga }}" <label class="flex items-center space-x-2">
{{ (is_array(old('fitur_id')) ? in_array($fiturItem->id, old('fitur_id')) : $template->fiturs->contains($fiturItem->id)) ? 'checked' : '' }}> <input type="radio"
<span class="text-sm"> name="fitur_id[{{ $kategoriFitur->id }}]"
{{ \Illuminate\Support\Str::limit($fiturItem->deskripsi, 80) }} value="{{ $fitur->id }}"
(Rp {{ number_format($fiturItem->harga,0,',','.') }}) data-harga="{{ $fitur->harga }}"
</span> @checked(old("fitur_id.$kategoriFitur->id") == $fitur->id)>
</label> <span class="text-sm">
@endforeach {{ \Illuminate\Support\Str::limit($fitur->deskripsi, 80) }}
</div> (Rp {{ number_format($fitur->harga, 0, ',', '.') }})
</div> </span>
</label>
<div> @endforeach
<label class="block text-sm font-medium">Harga</label>
<input type="number" name="harga" value="{{ old('harga', $template->harga) }}"
class="w-full p-2 border rounded" required min="0">
</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>
@endif </div>
<input type="file" name="foto" class="w-full p-2 border rounded" accept="image/*"> @endforeach
</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> {{-- Fitur tanpa kategori checkbox --}}
@if($fitursTanpaKategori->count())
<div>
<p class="font-semibold text-gray-700 mb-1">Fitur Lainnya</p>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2">
@foreach ($fitursTanpaKategori as $fitur)
<label class="flex items-center space-x-2">
<input type="checkbox" name="fitur_id[]"
value="{{ $fitur->id }}"
data-harga="{{ $fitur->harga }}"
{{ is_array(old('fitur_id')) && in_array($fitur->id, old('fitur_id')) ? 'checked' : '' }}>
<span class="text-sm">
{{ \Illuminate\Support\Str::limit($fitur->deskripsi, 80) }}
(Rp {{ number_format($fitur->harga, 0, ',', '.') }})
</span>
</label>
@endforeach
</div>
</div>
@endif
</div>
</div>
<div>
<label class="block text-sm font-medium">Harga</label>
<input type="text" name="harga" class="w-full p-2 border rounded" required
min="0" value="{{ old('harga') }}">
</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: JPG, PNG, GIF. Maks 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="{{ old('nama_template', $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" id="kategoriSelectEdit{{ $template->id }}" class="w-full p-2 border rounded">
<option value="">-- Pilih Kategori --</option>
@foreach ($kategoris as $kategoriItem)
<option value="{{ $kategoriItem->id }}"
@selected($kategoriItem->id == old('kategori_id', $template->kategori_id))>
{{ $kategoriItem->nama }}
</option>
@endforeach
</select>
</div>
<!-- FITUR -->
<div>
<label class="block text-sm font-medium">Fitur</label>
<div id="fiturContainerEdit{{ $template->id }}"
class="space-y-3 max-h-64 overflow-auto p-2 border rounded">
{{-- Fitur dengan kategori radio --}}
@foreach ($kategoriFiturs as $kategoriFitur)
<div>
<p class="font-semibold text-gray-700 mb-1">
{{ $kategoriFitur->deskripsi }}
</p>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2">
@foreach ($fiturs->where('kategori_fitur_id', $kategoriFitur->id) as $fiturItem)
<label class="flex items-center space-x-2">
<input type="radio"
name="fitur_id[{{ $kategoriFitur->id }}]"
value="{{ $fiturItem->id }}"
data-harga="{{ $fiturItem->harga }}"
{{ (old("fitur_id.$kategoriFitur->id") == $fiturItem->id) || $template->fiturs->contains($fiturItem->id) ? 'checked' : '' }}>
<span class="text-sm">
{{ \Illuminate\Support\Str::limit($fiturItem->deskripsi, 80) }}
(Rp {{ number_format($fiturItem->harga, 0, ',', '.') }})
</span>
</label>
@endforeach
</div>
</div>
@endforeach
{{-- Fitur tanpa kategori checkbox --}}
@if($fitursTanpaKategori->count())
<div>
<p class="font-semibold text-gray-700 mb-1">Fitur Lainnya</p>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2">
@foreach ($fitursTanpaKategori as $fitur)
<label class="flex items-center space-x-2">
<input type="checkbox" name="fitur_id[]"
value="{{ $fitur->id }}"
data-harga="{{ $fitur->harga }}"
{{ (is_array(old('fitur_id')) && in_array($fitur->id, old('fitur_id'))) || $template->fiturs->contains($fitur->id) ? 'checked' : '' }}>
<span class="text-sm">
{{ \Illuminate\Support\Str::limit($fitur->deskripsi, 80) }}
(Rp {{ number_format($fitur->harga, 0, ',', '.') }})
</span>
</label>
@endforeach
</div>
</div>
@endif
</div>
</div>
<div>
<label class="block text-sm font-medium">Harga</label>
<input type="number" name="harga" value="{{ old('harga', $template->harga) }}"
class="w-full p-2 border rounded" required min="0">
</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', () => { document.addEventListener('DOMContentLoaded', () => {
// ========================= // =========================
// Modal Tambah // Modal Tambah
@ -448,67 +521,84 @@ document.addEventListener('DOMContentLoaded', () => {
} }
// ========================= // =========================
// Fitur: Hitung Harga Otomatis + Format ribuan // Fitur: Hitung Harga Otomatis + Format ribuan
// ========================= // =========================
function formatRibuan(x) { function formatRibuan(x) {
if (!x) return ''; if (!x) return '';
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, "."); return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
}
function hitungHarga(container) {
let total = 0;
container.querySelectorAll('input[name="fitur_id[]"]:checked').forEach(cb => {
total += parseInt(cb.dataset.harga) || 0;
});
const inputHarga = container.querySelector('input[name="harga"]');
if (inputHarga && !inputHarga.dataset.manual) {
inputHarga.value = formatRibuan(total);
} }
}
// Update input text agar bisa ketik manual dengan format ribuan function hitungHarga(container) {
document.querySelectorAll('.harga-input').forEach(input => { let total = 0;
input.addEventListener('input', (e) => {
let value = e.target.value.replace(/\D/g, ''); // hanya angka // Hitung checkbox (fitur tanpa kategori)
e.target.value = formatRibuan(value); container.querySelectorAll('input[type="checkbox"][name="fitur_id[]"]:checked')
e.target.dataset.manual = "true"; // kasih flag manual .forEach(cb => {
total += parseInt(cb.dataset.harga) || 0;
});
// Hitung radio (fitur per kategori)
container.querySelectorAll('input[type="radio"]:checked')
.forEach(radio => {
total += parseInt(radio.dataset.harga) || 0;
});
const inputHarga = container.querySelector('input[name="harga"]');
if (inputHarga && !inputHarga.dataset.manual) {
inputHarga.value = formatRibuan(total);
}
}
// Format harga manual + tetap auto ribuan
document.querySelectorAll('input[name="harga"]').forEach(input => {
input.classList.add("harga-input"); // pastikan ada class
input.addEventListener('input', (e) => {
let value = e.target.value.replace(/\D/g, ''); // hanya angka
e.target.value = formatRibuan(value);
e.target.dataset.manual = "true"; // flag manual
});
}); });
});
// Modal Tambah // Modal Tambah
if (modalTambah) { if (modalTambah) {
const checkboxes = modalTambah.querySelectorAll('input[name="fitur_id[]"]'); const inputs = modalTambah.querySelectorAll('input[name="fitur_id[]"], input[type="radio"]');
const inputHarga = modalTambah.querySelector('input[name="harga"]'); const inputHarga = modalTambah.querySelector('input[name="harga"]');
checkboxes.forEach(cb => { inputs.forEach(input => {
cb.addEventListener('change', () => hitungHarga(modalTambah)); input.addEventListener('change', () => hitungHarga(modalTambah));
}); });
if (inputHarga) { if (inputHarga) {
inputHarga.addEventListener('input', () => { inputHarga.addEventListener('input', () => {
inputHarga.dataset.manual = "true"; // kasih flag manual inputHarga.dataset.manual = "true";
}); });
} }
// Hitung awal kalau ada yang sudah tercentang
hitungHarga(modalTambah);
} }
// Modal Edit (banyak, sesuai ID template) // Modal Edit (banyak)
document.querySelectorAll('[id^="modalEdit"]').forEach(modal => { document.querySelectorAll('[id^="modalEdit"]').forEach(modal => {
const checkboxes = modal.querySelectorAll('input[name="fitur_id[]"]'); const inputs = modal.querySelectorAll('input[name="fitur_id[]"], input[type="radio"]');
const inputHarga = modal.querySelector('input[name="harga"]'); const inputHarga = modal.querySelector('input[name="harga"]');
checkboxes.forEach(cb => { inputs.forEach(input => {
cb.addEventListener('change', () => hitungHarga(modal)); input.addEventListener('change', () => hitungHarga(modal));
}); });
if (inputHarga) { if (inputHarga) {
inputHarga.addEventListener('input', () => { inputHarga.addEventListener('input', () => {
inputHarga.dataset.manual = "true"; // kasih flag manual inputHarga.dataset.manual = "true";
}); });
} }
// Hitung awal kalau ada yang sudah tercentang
hitungHarga(modal);
}); });
}); });
</script> </script>
</div> </div>
@endsection @endsection