483 lines
28 KiB
PHP
483 lines
28 KiB
PHP
<link rel="stylesheet" href="{{ asset('assets/css/style.css') }}">
|
|
|
|
<div class="d-flex" style="min-height:100vh;">
|
|
|
|
{{-- SIDEBAR --}}
|
|
@include('sidebar')
|
|
|
|
{{-- CONTENT --}}
|
|
<div class="flex-grow-1 p-4">
|
|
<div class="container-fluid">
|
|
<div class="main-body">
|
|
|
|
<nav aria-label="breadcrumb" class="main-breadcrumb">
|
|
<ol class="breadcrumb">
|
|
<li class="breadcrumb-item active" aria-current="page">
|
|
Profil & Manajemen Pengguna
|
|
</li>
|
|
</ol>
|
|
</nav>
|
|
|
|
<div class="container-fluid">
|
|
{{-- Notifikasi Error Global --}}
|
|
@if ($errors->any())
|
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
|
<strong>Gagal!</strong> Periksa kembali inputan Anda.
|
|
<ul>
|
|
@foreach ($errors->all() as $error)
|
|
<li>{{ $error }}</li>
|
|
@endforeach
|
|
</ul>
|
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
|
<span aria-hidden="true">×</span>
|
|
</button>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- Form Update Profil --}}
|
|
<form action="{{ route('profil.update') }}" method="POST" enctype="multipart/form-data">
|
|
@csrf
|
|
@method('PUT')
|
|
|
|
<div class="row gutters-sm">
|
|
{{-- KIRI: FOTO PROFIL --}}
|
|
<div class="col-md-4 mb-3">
|
|
<div class="card shadow-sm">
|
|
<div class="card-body text-center">
|
|
<div class="d-flex flex-column align-items-center">
|
|
{{-- Preview Foto Otomatis --}}
|
|
<img id="previewFoto"
|
|
src="{{ Auth::user()->foto ? asset('foto_profil/' . Auth::user()->foto) : asset('img/default-avatar.png') }}"
|
|
class="rounded-circle mb-2" width="150" height="150"
|
|
style="object-fit: cover; border: 3px solid #f8f9fc;">
|
|
|
|
<div class="mt-3 w-100 text-left">
|
|
<label for="foto" class="small text-muted">Ganti Foto Profil</label>
|
|
<input type="file" name="foto" id="foto"
|
|
class="form-control form-control-sm @error('foto') is-invalid @enderror"
|
|
accept="image/*" onchange="previewImage()">
|
|
@error('foto')
|
|
<div class="invalid-feedback">{{ $message }}</div>
|
|
@enderror
|
|
</div>
|
|
|
|
<div class="mt-3">
|
|
<h4>{{ Auth::user()->nama_lengkap }}</h4>
|
|
<span class="badge badge-info">{{ strtoupper(Auth::user()->role) }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- KANAN: FORM DATA DIRI --}}
|
|
<div class="col-md-8">
|
|
<div class="card mb-3 shadow-sm border-0">
|
|
<div class="card-body">
|
|
{{-- Notifikasi Sukses --}}
|
|
@if (session('success'))
|
|
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
|
<i class="fas fa-check-circle mr-2"></i> {{ session('success') }}
|
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
|
<span aria-hidden="true">×</span>
|
|
</button>
|
|
</div>
|
|
@endif
|
|
|
|
@if (session('info'))
|
|
<div class="alert alert-info alert-dismissible fade show" role="alert">
|
|
<i class="fas fa-info-circle mr-2"></i> {{ session('info') }}
|
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
|
<span aria-hidden="true">×</span>
|
|
</button>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="row align-items-center">
|
|
<div class="col-sm-3"><strong>ID</strong></div>
|
|
<div class="col-sm-8 text-secondary">{{ Auth::user()->id }}</div>
|
|
<div class="col-sm-1 text-right"><i class="fas fa-lock text-muted"></i></div>
|
|
</div>
|
|
<hr>
|
|
|
|
<div class="row align-items-center">
|
|
<div class="col-sm-3"><strong>Username</strong></div>
|
|
<div class="col-sm-8">
|
|
{{-- Atribut 'readonly' membuat user tidak bisa mengetik, tapi data tetap terkirim --}}
|
|
<input type="text" name="username"
|
|
class="form-control form-control-sm bg-light @error('username') is-invalid @enderror"
|
|
value="{{ Auth::user()->username }}"
|
|
readonly>
|
|
</div>
|
|
{{-- Ikon diganti jadi gembok (fas fa-lock) agar user paham ini terkunci --}}
|
|
<div class="col-sm-1 text-right">
|
|
<i class="fas fa-lock text-muted"></i>
|
|
</div>
|
|
</div>
|
|
<hr>
|
|
|
|
<div class="row align-items-center">
|
|
<div class="col-sm-3"><strong>Password Baru</strong></div>
|
|
<div class="col-sm-8">
|
|
<input type="password" name="password" class="form-control form-control-sm"
|
|
placeholder="Kosongkan jika tidak ingin ganti password">
|
|
</div>
|
|
<div class="col-sm-1 text-right"><i class="fas fa-key text-primary"></i></div>
|
|
</div>
|
|
<hr>
|
|
|
|
<div class="row align-items-center">
|
|
<div class="col-sm-3"><strong>Nama Lengkap <span class="text-danger">*</span></strong></div>
|
|
<div class="col-sm-8">
|
|
<input type="text" name="nama_lengkap" class="form-control form-control-sm @error('nama_lengkap') is-invalid @enderror"
|
|
value="{{ old('nama_lengkap', Auth::user()->nama_lengkap) }}" required>
|
|
</div>
|
|
<div class="col-sm-1 text-right"><i class="fas fa-pencil-alt text-primary"></i></div>
|
|
</div>
|
|
<hr>
|
|
|
|
<div class="row align-items-center">
|
|
<div class="col-sm-3"><strong>Role</strong></div>
|
|
<div class="col-sm-8 text-secondary">{{ ucfirst(Auth::user()->role) }}</div>
|
|
<div class="col-sm-1 text-right"><i class="fas fa-lock text-muted"></i></div>
|
|
</div>
|
|
|
|
<div class="mt-4 text-right">
|
|
<button type="submit" class="btn btn-primary btn-sm px-4 shadow">
|
|
<i class="fas fa-save mr-1"></i> Simpan Semua Perubahan
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
{{-- Script Live Preview Gambar --}}
|
|
<script>
|
|
function previewImage() {
|
|
const foto = document.querySelector('#foto');
|
|
const imgPreview = document.querySelector('#previewFoto');
|
|
const oFReader = new FileReader();
|
|
oFReader.readAsDataURL(foto.files[0]);
|
|
oFReader.onload = function(oFREvent) {
|
|
imgPreview.src = oFREvent.target.result;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
{{-- TABEL DATA PENGGUNA --}}
|
|
<div class="modal fade" id="userModal" tabindex="-1" role="dialog" aria-hidden="true">
|
|
<div class="modal-dialog" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="modalTitle">Tambah Pengguna Baru</h5>
|
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
|
<span aria-hidden="true">×</span>
|
|
</button>
|
|
</div>
|
|
<form id="userForm" method="POST">
|
|
@csrf
|
|
<div id="methodPlaceholder"></div>
|
|
<div class="modal-body">
|
|
<div class="form-group">
|
|
<label>Username</label>
|
|
<input type="text" name="username" id="username" class="form-control" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Nama Lengkap</label>
|
|
<input type="text" name="nama_lengkap" id="nama_lengkap" class="form-control" required>
|
|
</div>
|
|
<div class="form-group" id="passwordGroup">
|
|
<label>Password</label>
|
|
<input type="password" name="password" id="password" class="form-control">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Role</label>
|
|
<select name="role" id="role" class="form-control" required>
|
|
<option value="user">USER</option>
|
|
<option value="admin">ADMIN</option>
|
|
<option value="super admin">SUPER ADMIN</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
|
|
<button type="submit" id="btnSubmit" class="btn btn-success">Tambah Pengguna</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row mt-4">
|
|
<div class="col-12">
|
|
<div class="card shadow mb-4">
|
|
<div class="card-header py-3 bg-white d-flex justify-content-between align-items-center">
|
|
<h6 class="m-0 font-weight-bold text-primary"><i class="fas fa-users mr-2"></i> Daftar Seluruh Pengguna</h6>
|
|
<button type="button" class="btn btn-success btn-sm btn-tambah">
|
|
<i class="fas fa-plus"></i> Tambah User
|
|
</button>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-bordered table-hover" width="100%">
|
|
<thead class="bg-light">
|
|
<tr>
|
|
<th width="5%">ID</th>
|
|
<th>Username</th>
|
|
<th>Nama Lengkap</th>
|
|
<th>Role</th>
|
|
<th class="text-center" width="15%">Aksi</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@forelse ($users as $u)
|
|
<tr>
|
|
<td>{{ $u->id }}</td>
|
|
<td>{{ $u->username }}</td>
|
|
<td>{{ $u->nama_lengkap }}</td>
|
|
<td>
|
|
<span class="badge {{ $u->role == 'admin' ? 'badge-danger' : 'badge-primary' }}">
|
|
{{ strtoupper($u->role) }}
|
|
</span>
|
|
</td>
|
|
<td class="text-center">
|
|
<button class="btn btn-warning btn-sm btn-edit"
|
|
data-id="{{ $u->id }}"
|
|
data-username="{{ $u->username }}"
|
|
data-nama="{{ $u->nama_lengkap }}"
|
|
data-role="{{ $u->role }}">
|
|
<i class="fas fa-edit text-white"></i>
|
|
</button>
|
|
<button class="btn btn-danger btn-sm btn-hapus-modal"
|
|
data-id="{{ $u->id }}"
|
|
data-nama="{{ $u->nama_lengkap }}">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="5" class="text-center">Data belum tersedia.</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- MODAL TAMBAH/EDIT USER --}}
|
|
<div class="modal fade" id="modalEditUser" tabindex="-1" role="dialog">
|
|
<div class="modal-dialog" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Edit Role Pengguna</h5>
|
|
</div>
|
|
<form id="formEditUser" method="POST">
|
|
@csrf
|
|
@method('PUT')
|
|
<div class="modal-body">
|
|
<div class="form-group">
|
|
<label>Username</label>
|
|
<input type="text" name="username" id="modal_username" class="form-control" readonly>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Nama Lengkap</label>
|
|
<input type="text" name="nama_lengkap" id="modal_nama" class="form-control" readonly>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Role</label>
|
|
<select name="role" id="modal_role" class="form-control">
|
|
<option value="user">USER</option>
|
|
<option value="admin">ADMIN</option>
|
|
<option value="super admin">SUPER ADMIN</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="submit" class="btn btn-primary">Simpan Perubahan</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- MODAL HAPUS --}}
|
|
|
|
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
|
<div class="modal-content text-center p-4">
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<div style="font-size: 50px; color: #ffbc8e; border: 4px solid #ffbc8e; border-radius: 50%; width: 100px; height: 100px; display: flex; align-items: center; justify-content: center; margin: 0 auto;">!</div>
|
|
</div>
|
|
<h2 class="font-weight-bold">Hapus User?</h2>
|
|
<p class="text-muted">Data user <b id="del_nama"></b> tidak dapat dikembalikan!</p>
|
|
<div class="mt-4">
|
|
<button type="button" class="btn btn-danger px-4" id="confirmDelete">Ya, Hapus!</button>
|
|
<button type="button" class="btn btn-secondary px-4 tutup-modal">Batal</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- SCRIPTS --}}
|
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
|
|
<script>
|
|
$(document).ready(function() {
|
|
let deleteId = null;
|
|
let originalData = {
|
|
username: '',
|
|
nama: '',
|
|
role: ''
|
|
};
|
|
|
|
// 1. Fungsi Tutup Modal
|
|
$(document).on('click', '.tutup-modal', function(e) {
|
|
e.preventDefault();
|
|
$('.modal').modal('hide');
|
|
$('body').removeClass('modal-open');
|
|
$('.modal-backdrop').remove();
|
|
});
|
|
|
|
// 2. Fungsi Reset Form
|
|
function resetFormState() {
|
|
$('#userForm')[0].reset();
|
|
$('#methodField').empty();
|
|
$('#passwordRow').show();
|
|
$('#modal_password').prop('required', true);
|
|
// Kembalikan tampilan input ke default
|
|
$('#modal_username, #modal_nama').prop('readonly', false).css('background-color', '#fff');
|
|
$('.form-control').removeClass('is-invalid');
|
|
}
|
|
|
|
// 3. Tombol Tambah (Membuka Modal)
|
|
function resetFormState() {
|
|
$('#userForm')[0].reset(); // Kosongkan semua input
|
|
$('#methodPlaceholder').html(''); // Hapus @method('PUT') jika sebelumnya habis edit
|
|
$('#passwordGroup').show(); // Tampilkan input password (karena biasanya kalau edit password dikosongkan)
|
|
$('#username').prop('readonly', false); // Pastikan username bisa diisi
|
|
}
|
|
|
|
$(document).ready(function() {
|
|
$('.btn-tambah').on('click', function() {
|
|
resetFormState();
|
|
$('#modalTitle').text('Tambah Pengguna Baru');
|
|
$('#userForm').attr('action', "{{ route('users.store') }}");
|
|
$('#btnSubmit').removeClass('btn-primary').addClass('btn-success').text('Tambah Pengguna');
|
|
$('#userModal').modal('show');
|
|
});
|
|
});
|
|
|
|
// 4. Tombol Edit (Membuka Modal & Isi Data)
|
|
$(document).ready(function() {
|
|
$(document).on('click', '.btn-edit', function() {
|
|
// Ambil data dari atribut tombol
|
|
var id = $(this).data('id');
|
|
var username = $(this).data('username');
|
|
var nama = $(this).data('nama'); // Pastikan di tombol ada data-nama
|
|
var role = $(this).data('role');
|
|
|
|
// Isi input modal
|
|
$('#modal_username').val(username);
|
|
$('#modal_nama').val(nama); // Mengisi Nama Lengkap
|
|
$('#modal_role').val(role);
|
|
|
|
// SET ACTION FORM (Penting untuk cegah 404)
|
|
// Pastikan url ini sama dengan yang ada di web.php
|
|
var actionUrl = "{{ url('admin/users/update') }}/" + id;
|
|
$('#formEditUser').attr('action', actionUrl);
|
|
|
|
// Tampilkan modal
|
|
$('#modalEditUser').modal('show');
|
|
});
|
|
});
|
|
|
|
// 5. VALIDASI SAAT SUBMIT (BAGIAN PALING PENTING)
|
|
$('#userForm').on('submit', function(e) {
|
|
const form = $(this); // Ambil form yang sedang aktif
|
|
const isEdit = $('#methodField').html().includes('PUT');
|
|
|
|
// Mengambil nilai input KHUSUS dari dalam form modal ini saja
|
|
const currentUsername = form.find('#modal_username').val() ? form.find('#modal_username').val().trim() : "";
|
|
const currentNama = form.find('#modal_nama').val() ? form.find('#modal_nama').val().trim() : "";
|
|
const currentRole = form.find('#modal_role').val();
|
|
const currentPass = form.find('#modal_password').val();
|
|
|
|
// Pengecekan apakah kosong (Cegah error "Gagal" yang anda alami)
|
|
if (currentUsername === "" || currentNama === "") {
|
|
e.preventDefault();
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: 'Gagal!',
|
|
text: 'Username dan Nama wajib diisi! Pastikan Anda telah mengetik dengan benar.',
|
|
confirmButtonColor: '#d33'
|
|
});
|
|
return false;
|
|
}
|
|
|
|
// Validasi minimal 3 karakter untuk Nama Lengkap
|
|
if (currentNama.length < 3) {
|
|
e.preventDefault();
|
|
Swal.fire({
|
|
icon: 'warning',
|
|
title: 'Perhatian',
|
|
text: 'Nama Lengkap minimal harus 3 karakter!'
|
|
});
|
|
return false;
|
|
}
|
|
|
|
// Jika sedang EDIT, cek apakah ada perubahan
|
|
if (isEdit) {
|
|
if (currentUsername === originalData.username &&
|
|
currentRole === originalData.role &&
|
|
currentPass === "" &&
|
|
currentNama === originalData.nama) {
|
|
|
|
e.preventDefault();
|
|
Swal.fire({
|
|
icon: 'info',
|
|
title: 'Tidak Ada Perubahan',
|
|
text: 'Anda belum mengubah data apapun pada user ini.',
|
|
confirmButtonColor: '#3085d6'
|
|
});
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Jika lolos semua validasi, form akan ter-submit secara otomatis
|
|
});
|
|
|
|
// 6. Tombol Hapus (Konfirmasi)
|
|
$(document).on('click', '.btn-hapus-modal', function() {
|
|
deleteId = $(this).data('id');
|
|
$('#del_nama').text($(this).data('nama'));
|
|
$('#deleteModal').modal('show');
|
|
});
|
|
|
|
$('#confirmDelete').on('click', function() {
|
|
const form = $('<form>', {
|
|
'action': "/users/" + deleteId,
|
|
'method': 'POST'
|
|
}).append('@csrf', '@method("DELETE")');
|
|
$('body').append(form);
|
|
form.submit();
|
|
});
|
|
|
|
// Otomatis reset saat modal ditutup
|
|
$('#userModal').on('hidden.bs.modal', function() {
|
|
resetFormState();
|
|
});
|
|
});
|
|
</script>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div> |