Files
database-pertani-web/resources/views/konfigurasi/user/index.blade.php
T
2026-04-21 10:10:09 +07:00

445 lines
21 KiB
PHP

@extends('layout.main')
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
<div class="row g-6 mb-6">
<div class="col-sm-6 col-xl-4">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span class="text-heading">Total Users</span>
<div class="d-flex align-items-center my-1">
<h4 class="mb-0 me-2">{{ number_format($totalUsers ?? 0) }}</h4>
</div>
<small class="mb-0">All registered accounts</small>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-label-primary">
<i class="icon-base ti tabler-users icon-26px"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-4">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span class="text-heading">Active Users</span>
<div class="d-flex align-items-center my-1">
<h4 class="mb-0 me-2">{{ number_format($activeUsers ?? 0) }}</h4>
</div>
<small class="mb-0">Verified Emails</small>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-label-success">
<i class="icon-base ti tabler-user-check icon-26px"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-4">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span class="text-heading">Pending Users</span>
<div class="d-flex align-items-center my-1">
<h4 class="mb-0 me-2">{{ number_format($pendingUsers ?? 0) }}</h4>
</div>
<small class="mb-0">Unverified Emails</small>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-label-warning">
<i class="icon-base ti tabler-user-search icon-26px"></i>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<ul class="nav nav-pills mb-4" role="tablist">
<li class="nav-item">
<a class="nav-link {{ $tab == 'active' ? 'active' : '' }}" href="{{ route('users.index', ['tab' => 'active', 'role' => request('role'), 'search' => request('search')]) }}">
<i class="ti tabler-user-check me-1"></i> Active Users
</a>
</li>
<li class="nav-item">
<a class="nav-link {{ $tab == 'pending' ? 'active' : '' }}" href="{{ route('users.index', ['tab' => 'pending', 'role' => request('role'), 'search' => request('search')]) }}">
<i class="ti tabler-clock me-1"></i> Pending Verifications
@if(($pendingUsers ?? 0) > 0)
<span class="badge rounded-pill badge-center h-px-20 w-px-20 bg-danger ms-1">{{ $pendingUsers }}</span>
@endif
</a>
</li>
</ul>
<div class="card">
<div class="card-header border-bottom d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">Daftar Pengguna</h5>
<button class="btn btn-sm btn-primary" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasAddUser">
<i class="ti tabler-plus me-1"></i> Add New User
</button>
</div>
<form method="GET" action="{{ route('users.index') }}" id="filterForm">
<input type="hidden" name="tab" value="{{ $tab }}">
<div class="card-body border-bottom pt-4">
<div class="row g-3 align-items-center">
<div class="col-md-auto">
<div class="d-flex align-items-center">
<label class="me-2 text-nowrap">Show</label>
<select name="per_page" class="form-select form-select-sm w-auto" onchange="this.form.submit()">
<option value="10" {{ request('per_page') == 10 ? 'selected' : '' }}>10</option>
<option value="25" {{ request('per_page') == 25 ? 'selected' : '' }}>25</option>
<option value="50" {{ request('per_page') == 50 ? 'selected' : '' }}>50</option>
</select>
<label class="ms-2 text-nowrap">entries</label>
</div>
</div>
<div class="col-md-3">
<select name="role" class="form-select form-select-sm" onchange="this.form.submit()">
<option value="">-- All Roles --</option>
@foreach($roles as $roleEnum)
<option value="{{ $roleEnum->value }}" {{ request('role') == $roleEnum->value ? 'selected' : '' }}>
{{ ucfirst(str_replace('_', ' ', $roleEnum->value)) }}
</option>
@endforeach
</select>
</div>
<div class="col-md-auto ms-auto">
<div class="input-group input-group-sm">
<input type="text" name="search" class="form-control" placeholder="Search name or email..." value="{{ request('search') }}">
<button type="submit" class="btn btn-primary"><i class="ti tabler-search"></i></button>
@if(request()->anyFilled(['search', 'role']))
<a href="{{ route('users.index', ['tab' => $tab]) }}" class="btn btn-label-secondary" title="Reset Filters"><i class="ti tabler-refresh"></i></a>
@endif
</div>
</div>
</div>
</div>
</form>
<div class="table-responsive text-nowrap">
<table class="table table-hover">
<thead class="border-top">
<tr>
<th>No</th>
<th>User</th>
<th>Role</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@forelse($users as $index => $user)
<tr>
<td>{{ $users->firstItem() + $index }}</td>
<td>
<div class="d-flex justify-content-start align-items-center user-name">
<div class="avatar-wrapper me-3">
<div class="avatar avatar-sm">
<span class="avatar-initial rounded-circle bg-label-dark">{{ strtoupper(substr($user->name, 0, 2)) }}</span>
</div>
</div>
<div class="d-flex flex-column">
<span class="fw-medium text-heading">{{ $user->name }}</span>
<small class="text-muted">{{ $user->email }}</small>
</div>
</div>
</td>
<td>
@php
$roleKey = ($user->role instanceof \App\Enums\Role) ? $user->role->value : $user->role;
$roleColors = [
'super_admin' => 'bg-label-danger',
'admin' => 'bg-label-warning',
'fasilitator' => 'bg-label-info',
'petani' => 'bg-label-success',
];
$color = $roleColors[$roleKey] ?? 'bg-label-secondary';
@endphp
<span class="badge {{ $color }}">
{{ ucfirst(str_replace('_', ' ', $roleKey)) }}
</span>
</td>
<td>
@if($user->email_verified_at)
<span class="badge bg-label-success">Verified</span>
@else
<span class="badge bg-label-warning">Pending</span>
<form action="{{ route('users.verify', $user->id) }}" method="POST" class="d-inline">
@csrf
@method('PATCH')
<button type="button" class="btn btn-xs btn-outline-success ms-2 btn-verify">Verify</button>
</form>
</form>
@endif
</td>
<td>
<div class="d-flex align-items-center">
<a href="javascript:;" class="btn btn-icon btn-text-secondary rounded-pill edit-record" title="Edit" data-id="{{ $user->id }}"><i class="ti tabler-edit ti-md"></i></a>
<a href="javascript:;" class="btn btn-icon btn-text-secondary rounded-pill delete-record" data-id="{{ $user->id }}" data-name="{{ $user->name }}" title="Delete"><i class="ti tabler-trash ti-md"></i></a>
</div>
</td>
</tr>
@empty
<tr><td colspan="5" class="text-center py-4">No users found.</td></tr>
@endforelse
</tbody>
</table>
</div>
<div class="card-footer d-flex justify-content-center justify-content-md-end border-top">
{{ $users->appends(request()->query())->links('pagination::bootstrap-5') }}
</div>
</div>
@include('konfigurasi.user.add')
@include('konfigurasi.user.edit')
</div>
@endsection
@push('scripts')
<script>
'use strict';
const csrfToken = "{{ csrf_token() }}";
$(document).ready(function () {
$(document).on('click', '.btn-verify', function (e) {
e.preventDefault();
const form = $(this).closest('form');
const url = form.attr('action');
const formData = new FormData(form[0]);
Swal.fire({
title: 'Verifikasi User?',
text: "User ini akan diaktifkan dan dapat login ke dalam sistem.",
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Ya, Verifikasi!',
cancelButtonText: 'Batal',
customClass: {
confirmButton: 'btn btn-success me-3',
cancelButton: 'btn btn-label-secondary'
},
buttonsStyling: false
}).then(function (result) {
if (result.value) {
$.ajax({
url: url,
type: 'POST',
data: formData,
processData: false,
contentType: false,
headers: {
'Accept': 'application/json'
},
success: function (res) {
if (res.status === 'success') {
Swal.fire({
icon: 'success',
title: 'Berhasil!',
text: res.message,
showConfirmButton: false,
timer: 1500
}).then(() => {
window.location.reload();
});
} else {
Swal.fire({ icon: 'error', title: 'Gagal!', text: res.message });
}
},
error: function (xhr) {
let errorMsg = xhr.responseJSON ? xhr.responseJSON.message : 'Terjadi kesalahan pada server.';
Swal.fire({ icon: 'error', title: 'Error!', text: errorMsg });
}
});
}
});
});
const offcanvasEditEl = document.getElementById('offcanvasEdit');
let bsOffcanvasEdit;
if (offcanvasEditEl) {
bsOffcanvasEdit = new bootstrap.Offcanvas(offcanvasEditEl);
}
$(document).on('click', '.edit-record', function (e) {
e.preventDefault();
const id = $(this).data('id');
const offcanvasEditEl = document.getElementById('offcanvasEdit');
if (!id) return;
$(this).html('<span class="spinner-border spinner-border-sm" role="status"></span>');
const btn = $(this);
$.ajax({
url: `/users/${id}`,
type: 'GET',
success: function (res) {
btn.html('<i class="ti tabler-edit ti-md"></i>');
if (res.status === 'success') {
const user = res.data;
$('#editUserName').text(user.name);
$('#editEmail').text(user.email);
$('#formEdit').attr('action', `/users/${user.id}`);
$('#formEdit')[0].reset();
const bsOffcanvasEdit = bootstrap.Offcanvas.getOrCreateInstance(offcanvasEditEl);
bsOffcanvasEdit.show();
}
},
error: function () {
btn.html('<i class="ti tabler-edit ti-md"></i>');
Swal.fire({ icon: 'error', title: 'Error', text: 'Gagal mengambil data user.' });
}
});
});
$('#formEdit').on('submit', function (e) {
e.preventDefault();
const form = $(this);
const submitBtn = form.find('button[type="submit"]');
submitBtn.prop('disabled', true).text('Menyimpan...');
$.ajax({
url: form.attr('action'),
type: 'POST',
data: form.serialize(),
success: function (res) {
if (res.status === 'success') {
bootstrap.Offcanvas.getInstance(document.getElementById('offcanvasEdit')).hide();
Swal.fire({ icon: 'success', title: 'Berhasil!', text: res.message, showConfirmButton: false, timer: 1500 })
.then(() => window.location.reload());
}
},
error: function (xhr) {
submitBtn.prop('disabled', false).text('Update Password');
let errorMsg = xhr.responseJSON?.message || 'Gagal mengupdate password.';
if (xhr.responseJSON?.errors) {
errorMsg = Object.values(xhr.responseJSON.errors).map(err => err.join(', ')).join('<br>');
}
Swal.fire({ icon: 'error', title: 'Gagal!', html: errorMsg });
}
});
});
$(document).on('click', '.delete-record', function () {
const id = $(this).data('id');
const name = $(this).data('name');
Swal.fire({
title: 'Hapus Pengguna?',
text: `Apakah kamu yakin ingin menghapus ${name}?`,
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, hapus!',
cancelButtonText: 'Batal',
customClass: { confirmButton: 'btn btn-danger me-3', cancelButton: 'btn btn-label-secondary' },
buttonsStyling: false
}).then(function (result) {
if (result.value) {
$.ajax({
url: `/users/${id}`, // Sesuaikan dengan route destroy kamu
type: 'DELETE',
headers: { 'X-CSRF-TOKEN': csrfToken },
success: function (res) {
Swal.fire({ icon: 'success', title: 'Berhasil dihapus!', text: res.message, showConfirmButton: false, timer: 1500 })
.then(() => window.location.reload());
},
error: () => Swal.fire({ icon: 'error', title: 'Error!', text: 'Failed to delete data.' })
});
}
});
});
});
document.addEventListener("DOMContentLoaded", function() {
function togglePassword(element) {
const input = element.parentElement.querySelector('input');
const icon = element.querySelector('i');
if (input.type === 'password') {
input.type = 'text';
icon.className = 'ti ti-eye tabler-eye';
} else {
input.type = 'password';
icon.className = 'ti ti-eye-off tabler-eye-off';
}
}
const formAddUser = document.getElementById('formAddUser');
const offcanvasElement = document.getElementById('offcanvasAddUser');
if (formAddUser && offcanvasElement) {
formAddUser.addEventListener('submit', function(e) {
e.preventDefault();
let formData = new FormData(this);
let submitBtn = this.querySelector('button[type="submit"]');
submitBtn.disabled = true;
let originalText = submitBtn.innerHTML;
submitBtn.innerHTML = 'Menyimpan...';
fetch(this.action, {
method: 'POST',
body: formData,
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Accept': 'application/json'
}
})
.then(response => response.json())
.then(data => {
submitBtn.disabled = false;
submitBtn.innerHTML = originalText;
if (data.status === 'success') {
let bsOffcanvas = bootstrap.Offcanvas.getInstance(offcanvasElement) || new bootstrap.Offcanvas(offcanvasElement);
bsOffcanvas.hide();
Swal.fire({
icon: 'success',
title: 'Berhasil!',
text: data.message,
showConfirmButton: false,
timer: 1500
}).then(() => {
window.location.reload();
});
} else {
let errorMsg = data.message || 'Terjadi kesalahan.';
if (data.errors) {
errorMsg = Object.values(data.errors).map(err => err.join(', ')).join('<br>');
}
Swal.fire({ icon: 'error', title: 'Gagal!', html: errorMsg });
}
})
.catch(error => {
submitBtn.disabled = false;
submitBtn.innerHTML = originalText;
Swal.fire({ icon: 'error', title: 'Error', text: 'Koneksi bermasalah atau terjadi kesalahan server.' });
});
});
}
});
</script>
@endpush