✨ feat(slik): implementasi fitur laporan SLIK dengan integrasi SweetAlert
- Menambahkan tombol aksi "Pindahkan ke Laporan SLIK" pada halaman index & detail - Integrasi SweetAlert2 untuk konfirmasi, loading state, dan notifikasi sukses/gagal - Implementasi auto-refresh DataTable setelah pemindahan berhasil - Disable tombol otomatis setelah sukses untuk mencegah duplikasi data - LaporanSlikController: method store() dengan transaksi DB & auto-delete dari tabel sliks - Routing baru untuk index, datatables, store, dan export laporan SLIK - Penyesuaian views (index & show) dengan tombol, script SweetAlert, dan feedback visual - Proteksi keamanan: CSRF token, validasi input, transaksi DB, dan error logging - Testing checklist: pindahkan data, refresh tabel, disable tombol, error handling, responsif mobile/desktop
This commit is contained in:
13
module.json
13
module.json
@@ -445,6 +445,19 @@
|
|||||||
"administrator",
|
"administrator",
|
||||||
"admin"
|
"admin"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Laporan SLIK",
|
||||||
|
"path": "admin-kredit.laporan-slik",
|
||||||
|
"icon": "ki-filled ki-filter-tablet text-lg text-primary",
|
||||||
|
"classes": "",
|
||||||
|
"attributes": [],
|
||||||
|
"permission": "",
|
||||||
|
"roles": [
|
||||||
|
"adk",
|
||||||
|
"administrator",
|
||||||
|
"admin"
|
||||||
|
]
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -169,7 +169,6 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@push('styles')
|
@push('styles')
|
||||||
@@ -212,16 +211,8 @@
|
|||||||
|
|
||||||
@push('scripts')
|
@push('scripts')
|
||||||
<script>
|
<script>
|
||||||
/**
|
let dataTable;
|
||||||
* Fungsi untuk menampilkan detail data SLIK
|
|
||||||
* @param {number} id - ID data SLIK yang akan ditampilkan
|
|
||||||
*/
|
|
||||||
function showDetail(id) {
|
|
||||||
// Redirect ke halaman detail
|
|
||||||
window.location.href = `{{ route('admin-kredit.slik.show', ':id') }}`.replace(':id', id);
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
/**
|
/**
|
||||||
* Inisialisasi DataTable untuk SLIK menggunakan KTDataTable
|
* Inisialisasi DataTable untuk SLIK menggunakan KTDataTable
|
||||||
@@ -229,9 +220,6 @@
|
|||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
const element = document.querySelector('#slik-table');
|
const element = document.querySelector('#slik-table');
|
||||||
const searchInput = document.getElementById('search');
|
const searchInput = document.getElementById('search');
|
||||||
const yearFilter = document.getElementById('year-filter');
|
|
||||||
const monthFilter = document.getElementById('month-filter');
|
|
||||||
const statusFilter = document.getElementById('status-filter');
|
|
||||||
|
|
||||||
const apiUrl = element.getAttribute('data-api-url');
|
const apiUrl = element.getAttribute('data-api-url');
|
||||||
|
|
||||||
@@ -301,6 +289,9 @@
|
|||||||
render: (item, data) => {
|
render: (item, data) => {
|
||||||
return `
|
return `
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
|
<button class="btn btn-sm btn-primary" onclick="moveToLaporan(${data.id})" title="SLIK">
|
||||||
|
<i class="ki-filled ki-file-up"></i> SLIK
|
||||||
|
</button>
|
||||||
<button class="btn btn-sm btn-light btn-icon" onclick="showDetail(${data.id})" title="Detail">
|
<button class="btn btn-sm btn-light btn-icon" onclick="showDetail(${data.id})" title="Detail">
|
||||||
<i class="ki-filled ki-eye"></i>
|
<i class="ki-filled ki-eye"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -312,7 +303,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Inisialisasi DataTable
|
// Inisialisasi DataTable
|
||||||
let dataTable = new KTDataTable(element, dataTableOptions);
|
dataTable = new KTDataTable(element, dataTableOptions);
|
||||||
dataTable.showSpinner();
|
dataTable.showSpinner();
|
||||||
|
|
||||||
// Fungsi pencarian
|
// Fungsi pencarian
|
||||||
@@ -321,26 +312,6 @@
|
|||||||
dataTable.search(searchValue, true);
|
dataTable.search(searchValue, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Filter berdasarkan tahun
|
|
||||||
yearFilter.addEventListener('change', function() {
|
|
||||||
const yearValue = this.value;
|
|
||||||
dataTable.setParam('year', yearValue);
|
|
||||||
dataTable.reload();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Filter berdasarkan bulan
|
|
||||||
monthFilter.addEventListener('change', function() {
|
|
||||||
const monthValue = this.value;
|
|
||||||
dataTable.setParam('month', monthValue);
|
|
||||||
dataTable.reload();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Filter berdasarkan status
|
|
||||||
statusFilter.addEventListener('change', function() {
|
|
||||||
const statusValue = this.value;
|
|
||||||
dataTable.setParam('status', statusValue);
|
|
||||||
dataTable.reload();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Export Excel functionality
|
// Export Excel functionality
|
||||||
document.getElementById('export-excel').addEventListener('click', function(e) {
|
document.getElementById('export-excel').addEventListener('click', function(e) {
|
||||||
@@ -349,13 +320,95 @@
|
|||||||
// Build export URL with current filters
|
// Build export URL with current filters
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
if (searchInput.value) params.append('search', searchInput.value);
|
if (searchInput.value) params.append('search', searchInput.value);
|
||||||
if (yearFilter.value) params.append('year', yearFilter.value);
|
|
||||||
if (monthFilter.value) params.append('month', monthFilter.value);
|
|
||||||
if (statusFilter.value) params.append('status', statusFilter.value);
|
|
||||||
|
|
||||||
const exportUrl = '{{ route('admin-kredit.slik.export') }}?' + params.toString();
|
const exportUrl = '{{ route('admin-kredit.slik.export') }}?' + params.toString();
|
||||||
window.open(exportUrl, '_blank');
|
window.open(exportUrl, '_blank');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* Fungsi untuk menampilkan detail data SLIK
|
||||||
|
* @param {number} id - ID data SLIK yang akan ditampilkan
|
||||||
|
*/
|
||||||
|
function showDetail(id) {
|
||||||
|
// Redirect ke halaman detail
|
||||||
|
window.location.href = `{{ route('admin-kredit.slik.show', ':id') }}`.replace(':id', id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fungsi untuk memindahkan data SLIK ke laporan
|
||||||
|
* @param {number} id - ID data SLIK yang akan dipindahkan
|
||||||
|
*/
|
||||||
|
function moveToLaporan(id) {
|
||||||
|
window.Swal.fire({
|
||||||
|
title: 'Konfirmasi',
|
||||||
|
text: 'Apakah Anda yakin ingin memindahkan data ini ke laporan SLIK?',
|
||||||
|
icon: 'question',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: '#3085d6',
|
||||||
|
cancelButtonColor: '#d33',
|
||||||
|
confirmButtonText: 'Ya, Pindahkan!',
|
||||||
|
cancelButtonText: 'Batal',
|
||||||
|
reverseButtons: true
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
// Tampilkan loading
|
||||||
|
window.Swal.fire({
|
||||||
|
title: 'Memproses...',
|
||||||
|
text: 'Sedang memindahkan data ke laporan SLIK',
|
||||||
|
allowOutsideClick: false,
|
||||||
|
showConfirmButton: false,
|
||||||
|
willOpen: () => {
|
||||||
|
window.Swal.showLoading();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fetch(`{{ route('admin-kredit.laporan-slik.store') }}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
slik_id: id
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
window.Swal.close();
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
window.Swal.fire({
|
||||||
|
title: 'Berhasil!',
|
||||||
|
text: data.message || 'Data berhasil dipindahkan ke laporan SLIK',
|
||||||
|
icon: 'success',
|
||||||
|
confirmButtonText: 'OK'
|
||||||
|
}).then(() => {
|
||||||
|
// Reload tabel DataTable
|
||||||
|
dataTable.reload();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
window.Swal.fire({
|
||||||
|
title: 'Gagal!',
|
||||||
|
text: data.message || 'Gagal memindahkan data',
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'OK'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
window.Swal.close();
|
||||||
|
window.Swal.fire({
|
||||||
|
title: 'Error!',
|
||||||
|
text: 'Terjadi kesalahan saat memindahkan data',
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'OK'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@endpush
|
@endpush
|
||||||
|
|||||||
@@ -14,6 +14,11 @@
|
|||||||
<p class="mt-1 text-sm text-gray-600">Informasi lengkap debitur {{ $slik->nama_debitur }}</p>
|
<p class="mt-1 text-sm text-gray-600">Informasi lengkap debitur {{ $slik->nama_debitur }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
|
<button class="btn btn-sm btn-primary" onclick="moveToLaporan({{ $slik->id }})"
|
||||||
|
title="Pindahkan ke Laporan SLIK">
|
||||||
|
<i class="ki-filled ki-file-up"></i>
|
||||||
|
SLIK
|
||||||
|
</button>
|
||||||
<a href="{{ route('admin-kredit.slik.index') }}" class="btn btn-sm btn-light">
|
<a href="{{ route('admin-kredit.slik.index') }}" class="btn btn-sm btn-light">
|
||||||
<i class="ki-filled ki-arrow-left"></i>
|
<i class="ki-filled ki-arrow-left"></i>
|
||||||
Kembali
|
Kembali
|
||||||
@@ -165,6 +170,87 @@
|
|||||||
|
|
||||||
@push('scripts')
|
@push('scripts')
|
||||||
<script>
|
<script>
|
||||||
|
/**
|
||||||
|
* Fungsi untuk memindahkan data SLIK ke laporan
|
||||||
|
* @param {number} id - ID data SLIK yang akan dipindahkan
|
||||||
|
*/
|
||||||
|
function moveToLaporan(id) {
|
||||||
|
window.Swal.fire({
|
||||||
|
title: 'Konfirmasi',
|
||||||
|
text: 'Apakah Anda yakin ingin memindahkan data ini ke laporan SLIK?',
|
||||||
|
icon: 'question',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: '#3085d6',
|
||||||
|
cancelButtonColor: '#d33',
|
||||||
|
confirmButtonText: 'Ya, Pindahkan!',
|
||||||
|
cancelButtonText: 'Batal',
|
||||||
|
reverseButtons: true
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
// Tampilkan loading
|
||||||
|
window.Swal.fire({
|
||||||
|
title: 'Memproses...',
|
||||||
|
text: 'Sedang memindahkan data ke laporan SLIK',
|
||||||
|
allowOutsideClick: false,
|
||||||
|
showConfirmButton: false,
|
||||||
|
willOpen: () => {
|
||||||
|
window.Swal.showLoading();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fetch(`{{ route('admin-kredit.laporan-slik.store') }}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
slik_id: id
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
window.Swal.close();
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
window.Swal.fire({
|
||||||
|
title: 'Berhasil!',
|
||||||
|
text: data.message || 'Data berhasil dipindahkan ke laporan SLIK',
|
||||||
|
icon: 'success',
|
||||||
|
confirmButtonText: 'OK'
|
||||||
|
}).then(() => {
|
||||||
|
// Disable tombol setelah berhasil
|
||||||
|
const button = document.querySelector(
|
||||||
|
`button[onclick="moveToLaporan(${id})"]`);
|
||||||
|
if (button) {
|
||||||
|
button.disabled = true;
|
||||||
|
button.innerHTML =
|
||||||
|
'<i class="ki-filled ki-check"></i> Sudah di SLIK';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
window.Swal.fire({
|
||||||
|
title: 'Gagal!',
|
||||||
|
text: data.message || 'Gagal memindahkan data',
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'OK'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
window.Swal.close();
|
||||||
|
window.Swal.fire({
|
||||||
|
title: 'Error!',
|
||||||
|
text: 'Terjadi kesalahan saat memindahkan data',
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'OK'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Add any additional JavaScript for detail page
|
// Add any additional JavaScript for detail page
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
// Initialize any required components
|
// Initialize any required components
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ use Modules\Lpj\Http\Controllers\NocController;
|
|||||||
use Modules\Lpj\Http\Controllers\SLAController;
|
use Modules\Lpj\Http\Controllers\SLAController;
|
||||||
use Modules\Lpj\Http\Controllers\KJPPController;
|
use Modules\Lpj\Http\Controllers\KJPPController;
|
||||||
use Modules\Lpj\Http\Controllers\MemoController;
|
use Modules\Lpj\Http\Controllers\MemoController;
|
||||||
use Modules\Lpj\Http\Controllers\BucokController;
|
|
||||||
use Modules\Lpj\Http\Controllers\SlikController;
|
use Modules\Lpj\Http\Controllers\SlikController;
|
||||||
|
use Modules\Lpj\Http\Controllers\BucokController;
|
||||||
use Modules\Lpj\Http\Controllers\TeamsController;
|
use Modules\Lpj\Http\Controllers\TeamsController;
|
||||||
use Modules\Lpj\Http\Controllers\RegionController;
|
use Modules\Lpj\Http\Controllers\RegionController;
|
||||||
use Modules\Lpj\Http\Controllers\ResumeController;
|
use Modules\Lpj\Http\Controllers\ResumeController;
|
||||||
@@ -22,6 +22,7 @@ use Modules\Lpj\Http\Controllers\PenilaianController;
|
|||||||
use Modules\Lpj\Http\Controllers\PembatalanController;
|
use Modules\Lpj\Http\Controllers\PembatalanController;
|
||||||
use Modules\Lpj\Http\Controllers\PermohonanController;
|
use Modules\Lpj\Http\Controllers\PermohonanController;
|
||||||
use Modules\Lpj\Http\Controllers\CustomFieldController;
|
use Modules\Lpj\Http\Controllers\CustomFieldController;
|
||||||
|
use Modules\Lpj\Http\Controllers\LaporanSlikController;
|
||||||
use Modules\Lpj\Http\Controllers\LaporanUserController;
|
use Modules\Lpj\Http\Controllers\LaporanUserController;
|
||||||
use Modules\Lpj\Http\Controllers\JenisDokumenController;
|
use Modules\Lpj\Http\Controllers\JenisDokumenController;
|
||||||
use Modules\Lpj\Http\Controllers\JenisJaminanController;
|
use Modules\Lpj\Http\Controllers\JenisJaminanController;
|
||||||
@@ -805,6 +806,14 @@ Route::middleware(['auth'])->group(function () {
|
|||||||
Route::delete('truncate', [SlikController::class, 'truncate'])->name('truncate');
|
Route::delete('truncate', [SlikController::class, 'truncate'])->name('truncate');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Route Laporan SLIK
|
||||||
|
Route::prefix('laporan-slik')->name('laporan-slik.')->group(function () {
|
||||||
|
Route::get('/', [LaporanSlikController::class, 'index'])->name('index');
|
||||||
|
Route::get('datatables', [LaporanSlikController::class, 'datatables'])->name('datatables');
|
||||||
|
Route::post('store', [LaporanSlikController::class, 'store'])->name('store');
|
||||||
|
Route::get('export', [LaporanSlikController::class, 'export'])->name('export');
|
||||||
|
});
|
||||||
|
|
||||||
// Laporan Routes
|
// Laporan Routes
|
||||||
Route::prefix('laporan')->name('laporan.')->group(function () {
|
Route::prefix('laporan')->name('laporan.')->group(function () {
|
||||||
Route::get('/', [LaporanAdminKreditController::class, 'index'])->name('index');
|
Route::get('/', [LaporanAdminKreditController::class, 'index'])->name('index');
|
||||||
|
|||||||
Reference in New Issue
Block a user