373 lines
18 KiB
PHP
373 lines
18 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 Pengguna</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">Semua Akun Terdaftar</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">Pengguna Aktif</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">Email Terverifikasi</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">Pengguna Tertunda</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">Emails Belum Terverifikasi</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>
|
|
<a class="btn btn-sm btn-primary" href="{{ route('users.create') }}">
|
|
<i class="ti tabler-plus me-1"></i> Tambah User Baru
|
|
</a>
|
|
</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.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.' })
|
|
});
|
|
}
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
@endpush
|