Hidupkan CRUD level-user: store/show/update/destroy + inline form + cegah hapus level terpakai

This commit is contained in:
2026-10-14 11:35:28 +07:00
parent f3818f03f5
commit ff8fc51500
3 changed files with 120 additions and 92 deletions
@@ -7,6 +7,7 @@
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class LevelUserController extends Controller
@@ -43,7 +44,24 @@ public function create()
*/
public function store(Request $request)
{
//
$data = $request->validate(
[
'level_code' => 'required|string|max:10|unique:level_user,level_code',
'level_name' => 'required|string|max:30',
],
[
'level_code.unique' => 'Kode level sudah dipakai, ganti dengan yang unik.',
]
);
$level = LevelPegawai::create([
'level_code' => $data['level_code'],
'level_name' => $data['level_name'],
'create_by' => Auth::user()->nomer ?? Auth::user()->username ?? 'system',
'create_date' => now(),
]);
return response()->json(['success' => true, 'message' => 'Level pengguna berhasil ditambahkan.', 'data' => $level], 201);
}
/**
@@ -54,7 +72,7 @@ public function store(Request $request)
*/
public function show($id)
{
Log::info($id);
return response()->json(['success' => true, 'data' => LevelPegawai::findOrFail($id)]);
}
/**
@@ -65,7 +83,7 @@ public function show($id)
*/
public function edit($id)
{
//
return response()->json(['success' => true, 'data' => LevelPegawai::findOrFail($id)]);
}
/**
@@ -77,7 +95,17 @@ public function edit($id)
*/
public function update(Request $request, $id)
{
//
// level_code tidak bisa diubah (dipakai relasi pegawai.level_pegawai)
$data = $request->validate(['level_name' => 'required|string|max:30']);
$level = LevelPegawai::findOrFail($id);
$level->update([
'level_name' => $data['level_name'],
'update_by' => Auth::id(),
'update_date' => now(),
]);
return response()->json(['success' => true, 'message' => 'Level pengguna berhasil diperbarui.', 'data' => $level]);
}
/**
@@ -88,6 +116,18 @@ public function update(Request $request, $id)
*/
public function destroy($id)
{
//
$level = LevelPegawai::findOrFail($id);
$dipakai = DB::table('pegawai')->where('level_pegawai', $level->level_code)->count();
if ($dipakai > 0) {
return response()->json([
'success' => false,
'message' => "Level {$level->level_code} dipakai {$dipakai} pegawai, tidak bisa dihapus.",
], 409);
}
$level->delete();
return response()->json(['success' => true, 'message' => 'Level pengguna berhasil dihapus.']);
}
}
+2
View File
@@ -10,6 +10,8 @@ class LevelPegawai extends Model
use HasFactory;
protected $table = 'level_user';
protected $primaryKey = 'level_id';
public $timestamps = false; // pakai create_date / update_date manual
protected $fillable = [
'level_code', 'level_name','create_by','create_date','update_date','update_by'
@@ -56,9 +56,9 @@
<div class="card shadow-sm h-100">
<div class="card">
<div class="card-body">
<h4 class="card-title">Daftar Pintu/Pos</h4>
<h4 class="card-title">Daftar Level Pengguna</h4>
<div class="table-responsive mb-2">
<table class="table table-bordered table-selectable" id="example" data-url="">
<table class="table table-bordered table-selectable" id="level-table" data-url="">
<thead>
<tr>
<th class="p-3" width="3%">No.</th>
@@ -73,9 +73,9 @@
<tr>
<td class="text-center">{{ $loop->iteration }}</td>
<td class="text-center" hidden>{{ $level->level_id }}</td>
<td class="text-center" width="7%">{{ $level->level_code }} </td>
<td class="text-center" width="7%" data-level-id="{{ $level->level_id }}" data-level-code="{{ $level->level_code }}" data-level-name="{{ $level->level_name }}">{{ $level->level_code }} </td>
<td>{{ $level->level_name }}</td>
<td class="text-center" width="10%">{{ \Carbon\Carbon::parse($level->create_date)->format('Y-m-d')}}</td>
<td class="text-center" width="10%">{{ $level->create_date ? \Carbon\Carbon::parse($level->create_date)->format('Y-m-d') : '-' }}</td>
<td class="text-center" width="12%">
<button type="button" class="user-acess" data-bs-toggle="modal" title="Izin Akses" data-bs-target=".bd-ubah-modal-lg"><i class="fa-solid fa-user-gear"></i></button>
<button type="button" class="user-menu" data-bs-toggle="modal" id="menuakses" title="Menu Akses" data-bs-target=".user-menu-modal-lg"><i class="fa-solid fa-bars"></i></button>
@@ -86,11 +86,11 @@
</table>
</div>
<div class="d-flex justify-content-center align-items-center mt-2">
<div>
<button class="btn btn-primary btn-sm add-button" data-bs-toggle="modal" data-bs-target=".bd-example-modal-lg">Tambah</button>
<button class="btn btn-warning btn-sm edit-button" id="btnEdit" data-bs-toggle="modal" data-bs-target=".bd-ubah-modal-lg">Ubah</button>
<button class="btn btn-danger btn-sm" id="btnDelete">Hapus</button>
<button class="btn btn-danger btn-sm" id="btnDelete">Detail</button>
<div class="d-flex gap-2">
<button class="btn btn-primary btn-sm" id="btnAddLevel" type="button">Tambah</button>
<button class="btn btn-warning btn-sm" id="btnEditLevel" type="button" disabled>Ubah</button>
<button class="btn btn-danger btn-sm" id="btnDeleteLevel" type="button" disabled>Hapus</button>
<button class="btn btn-info btn-sm" id="btnDetailLevel" type="button" disabled>Detail</button>
</div>
</div>
</div>
@@ -102,6 +102,12 @@
<!-- END MEMMBER MODAL -->
</div>
</div>
<div class="modal fade" id="level-detail-modal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered"><div class="modal-content">
<div class="modal-header"><h5 class="modal-title">Detail Level Pengguna</h5><button class="btn-close" data-bs-dismiss="modal"></button></div>
<div class="modal-body" id="level-detail-body"></div>
</div></div>
</div>
</div>
</div>
</div>
@@ -120,85 +126,65 @@
<script src="{{asset ('vendor/sweetalert2/dist/sweetalert2.min.js') }}"></script>
<script src="{{asset ('js/custom.min.js') }}"></script>
<script src="{{asset ('js/dlabnav-init.js') }}"></script>
{{-- <script>
$(document).ready(function () {
<script>
$(function () {
let selected = null;
const csrf = $('meta[name="csrf-token"]').attr('content');
const routes = {
store: '{{ route('levelUser.store', [], false) }}',
show: '{{ route('levelUser.show', ':id', false) }}',
update: '{{ route('levelUser.update', ':id', false) }}',
destroy: '{{ route('levelUser.destroy', ':id', false) }}'
};
$(document).on('click', '.user-menu', function () {
let id = $(this).data('id')
let url = "{{ route() }}"
});
// MODE EDIT
$(document).on('click', '.btn-edit', function () {
$('.view-mode').addClass('d-none');
$('.edit-mode').removeClass('d-none');
$('.btn-edit').addClass('d-none');
$('.btn-save, .btn-cancel').removeClass('d-none');
});
// CANCEL
$(document).on('click', '.btn-cancel', function () {
$('.edit-mode').addClass('d-none');
$('.view-mode').removeClass('d-none');
$('.btn-save, .btn-cancel').addClass('d-none');
$('.btn-edit').removeClass('d-none');
});
// SAVE
$(document).on('click', '.btn-save', function () {
let dataToSend = {};
$('.edit-mode').each(function () {
let key = $(this).data('key');
dataToSend[key] = $(this).val();
});
console.log(dataToSend);
let gateId = $('.edit-row').data('id');
$.ajax({
url: "{{ route('hardware.update', '') }}/" + gateId,
type: "PUT",
data: {
_token: "{{ csrf_token() }}",
data: dataToSend
},
success: function () {
$('.edit-mode').each(function () {
let val = $(this).val();
$(this).siblings('.view-mode').text(val);
});
$('.btn-cancel').click();
Swal.fire({icon: 'success', title: 'Berhasil', text: 'Level pengguna tersimpan.'});
}
});
});
function formatValue(key, value) {
// kalau field *_avail kita kasih badge
if (key.includes('_avail')) {
if (value == 1) {
return `<span class="badge rounded-pill bg-success px-3">ON</span>`;
} else {
return `<span class="badge rounded-pill bg-danger px-3">OFF</span>`;
}
}
// default
return value ?? '-';
function setSelected(row) {
$('#level-table tbody tr').removeClass('table-active');
selected = row ? $(row).find('[data-level-id]').data() : null;
if (row) $(row).addClass('table-active');
$('#btnDetailLevel, #btnEditLevel, #btnDeleteLevel').prop('disabled', !selected);
}
});
</script> --}}
function formRow(mode, level) {
const isEdit = mode === 'edit';
return `<tr class="level-form-row"><td colspan="5"><form class="level-inline-form" data-mode="${mode}" data-id="${level?.levelId || ''}"><div class="row g-2 align-items-end"><div class="col-md-3"><label class="form-label mb-1">Level Code</label><input class="form-control" name="level_code" maxlength="10" value="${level?.levelCode || ''}" ${isEdit ? 'readonly' : ''} required></div><div class="col-md-5"><label class="form-label mb-1">Level Pengguna</label><input class="form-control" name="level_name" maxlength="30" value="${level?.levelName || ''}" required></div><div class="col-md-4 d-flex gap-2"><button class="btn btn-success" type="submit">Simpan</button><button class="btn btn-light border cancel-level-form" type="button">Batal</button></div></div></form></td></tr>`;
}
$('#level-table tbody').on('click', 'tr:not(.level-form-row)', function () { setSelected(this); });
$('#btnAddLevel').on('click', function () { $('.level-form-row').remove(); $('#level-table tbody').prepend(formRow('create')); setSelected(null); });
$('#btnEditLevel').on('click', function () { if (!selected) return; $('.level-form-row').remove(); $('#level-table tbody tr').filter(function(){ return $(this).find('[data-level-id]').data('level-id') === selected.levelId; }).after(formRow('edit', selected)); });
$(document).on('click', '.cancel-level-form', function () { $(this).closest('tr').remove(); });
$(document).on('submit', '.level-inline-form', function (event) {
event.preventDefault(); const form = $(this); const id = form.data('id'); const edit = form.data('mode') === 'edit';
const btn = form.find('[type="submit"]'); const btnHtml = btn.html();
btn.prop('disabled', true).html('<span class="spinner-border spinner-border-sm me-1"></span>Menyimpan...');
$.ajax({
url: edit ? routes.update.replace(':id', encodeURIComponent(id)) : routes.store,
type: edit ? 'PUT' : 'POST', data: form.serialize(), headers: {'X-CSRF-TOKEN': csrf},
success: () => window.location.reload(),
error: xhr => {
btn.prop('disabled', false).html(btnHtml);
Swal.fire({icon: 'error', title: 'Gagal menyimpan', text: xhr.responseJSON?.message || Object.values(xhr.responseJSON?.errors || {}).flat().join('\n') || 'Gagal menyimpan level.'});
}
});
});
$('#btnDeleteLevel').on('click', function () {
if (!selected) return;
Swal.fire({title:'Hapus level?', text:`${selected.levelCode} - ${selected.levelName}`, icon:'warning', showCancelButton:true, confirmButtonText:'Ya, hapus', cancelButtonText:'Batal', confirmButtonColor:'#dc3545', showLoaderOnConfirm: true, preConfirm: () => $.ajax({url: routes.destroy.replace(':id', encodeURIComponent(selected.levelId)), type:'DELETE', headers:{'X-CSRF-TOKEN':csrf}}).catch(xhr => { Swal.showValidationMessage(xhr.responseJSON?.message || 'Level tidak dapat dihapus.'); })})
.then(result => { if (result.isConfirmed && result.value) { Swal.fire({icon: 'success', title: 'Berhasil', text: 'Level dihapus.', timer: 1500, showConfirmButton: false}).then(() => window.location.reload()); } });
});
$('#btnDetailLevel').on('click', function () {
if (!selected) return;
const btn = $(this); const btnHtml = btn.html();
btn.prop('disabled', true).html('<span class="spinner-border spinner-border-sm me-1"></span>Memuat...');
$('#level-detail-body').html('<div class="text-center py-3"><span class="spinner-border spinner-border-sm me-2"></span>Memuat detail...</div>');
new bootstrap.Modal(document.getElementById('level-detail-modal')).show();
$.get(routes.show.replace(':id', encodeURIComponent(selected.levelId)), function(r) {
$('#level-detail-body').html(`<dl class="row mb-0"><dt class="col-5">Level Code</dt><dd class="col-7">${r.data.level_code}</dd><dt class="col-5">Nama Level</dt><dd class="col-7">${r.data.level_name}</dd><dt class="col-5">Dibuat</dt><dd class="col-7">${r.data.create_date ?? '-'}</dd><dt class="col-5">Diubah</dt><dd class="col-7">${r.data.update_date ?? '-'}</dd></dl>`);
}).fail(xhr => { $('#level-detail-body').html('<div class="text-danger text-center">' + (xhr.responseJSON?.message || 'Gagal memuat detail.') + '</div>'); }).always(() => btn.prop('disabled', false).html(btnHtml));
});
});
</script>
@endsection