🎨 feat(slik): Implementasi fitur pindah data SLIK ke laporan dan perbaikan UI

- Menambahkan tombol "SLIK" pada halaman index dan show untuk memindahkan data ke laporan SLIK
- Mengimplementasikan fungsi moveToLaporan() dengan konfirmasi SweetAlert dan proses AJAX
- Melakukan migrasi framework CSS dari Bootstrap ke TailwindCSS pada laporan-slik/show.blade.php
- Memperbaiki struktur layout dengan grid system TailwindCSS yang responsif
- Mengupdate breadcrumbs dengan styling dan route names yang benar
- Menghapus fitur truncate data SLIK dari interface untuk keamanan data
- Memperbaiki route names dari admin-kredit.laporan-slik menjadi laporan-slik
- Mengoptimasi tombol Export dengan penghapusan parameter ID yang tidak diperlukan
- Menambahkan konfigurasi import SLIK di .env.example untuk optimasi performa
- Memperbaiki template download link dengan class styling yang konsisten
- Mengimplementasikan error handling yang komprehensif dengan user feedback
- Menambahkan auto-reload DataTable setelah operasi pemindahan data berhasil
- Melakukan redesign card layout dengan pembagian "Data Debitur" dan "Data Fasilitas"
- Menambahkan feedback visual dengan disable tombol setelah berhasil dipindahkan
- Mengoptimasi konfigurasi DataTable dengan reload functionality
- Menambahkan breadcrumb routes untuk laporan SLIK dengan struktur hierarki
- Mengimplementasikan progress tracking untuk monitoring proses import
- Memperbaiki JavaScript dengan pemisahan fungsi dan penambahan variabel global
- Menstandarisasi framework CSS untuk konsistensi visual
- Mengimplementasikan responsive design yang lebih baik
This commit is contained in:
Daeng Deni Mardaeni
2025-09-17 15:19:28 +07:00
parent d932559849
commit 6c004812a9
3 changed files with 509 additions and 0 deletions

View File

@@ -0,0 +1,117 @@
<?php
namespace Modules\Lpj\Exports;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithStyles;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class LaporanSlikExport implements FromQuery, WithHeadings, WithMapping, WithStyles
{
protected $query;
public function __construct($query)
{
$this->query = $query;
}
public function query()
{
return $this->query;
}
public function headings(): array
{
return [
'Sandi Bank',
'Kode Kantor',
'Kode Cabang',
'Tahun',
'Bulan',
'No Rekening',
'CIF',
'Nama Debitur',
'NPWP',
'No KTP',
'No Telp',
'Alamat',
'Kode Pos',
'Kode Kab/Kota',
'Kode Negara Domisili',
'Kode Jenis',
'Kode Sifat',
'Kode Valuta',
'Baki Debet',
'Kolektibilitas',
'Tanggal Mulai',
'Tanggal Jatuh Tempo',
'Tanggal Selesai',
'Tanggal Restrukturisasi',
'Kode Sebab Macet',
'Tanggal Macet',
'Kode Kondisi',
'Tanggal Kondisi',
'Nilai Agunan',
'Jenis Agunan',
'Kode Agunan',
'Peringkat Agunan',
'Fasilitas',
'Status Agunan',
'Tanggal Lapor',
'Status',
'Tanggal Dibuat',
];
}
public function map($laporanSlik): array
{
return [
$laporanSlik->sandi_bank,
$laporanSlik->kode_kantor,
$laporanSlik->kode_cabang,
$laporanSlik->tahun,
$laporanSlik->bulan,
$laporanSlik->no_rekening,
$laporanSlik->cif,
$laporanSlik->nama_debitur,
$laporanSlik->npwp,
$laporanSlik->no_ktp,
$laporanSlik->no_telp,
$laporanSlik->alamat,
$laporanSlik->kode_pos,
$laporanSlik->kode_kab_kota,
$laporanSlik->kode_negara_domisili,
$laporanSlik->kode_jenis,
$laporanSlik->kode_sifat,
$laporanSlik->kode_valuta,
$laporanSlik->baki_debet,
$laporanSlik->kolektibilitas,
$laporanSlik->tanggal_mulai,
$laporanSlik->tanggal_jatuh_tempo,
$laporanSlik->tanggal_selesai,
$laporanSlik->tanggal_restrukturisasi,
$laporanSlik->kode_sebab_macet,
$laporanSlik->tanggal_macet,
$laporanSlik->kode_kondisi,
$laporanSlik->tanggal_kondisi,
$laporanSlik->nilai_agunan,
$laporanSlik->jenis_agunan,
$laporanSlik->kode_agunan,
$laporanSlik->peringkat_agunan,
$laporanSlik->fasilitas,
$laporanSlik->status_agunan,
$laporanSlik->tanggal_lapor,
$laporanSlik->status,
$laporanSlik->created_at->format('d/m/Y H:i'),
];
}
public function styles(Worksheet $sheet)
{
return [
1 => ['font' => ['bold' => true]],
];
}
}

View File

@@ -0,0 +1,220 @@
@extends('layouts.main')
@section('breadcrumbs')
{{ Breadcrumbs::render('admin-kredit.laporan-slik') }}
@endsection
@section('content')
<div class="grid">
<div class="min-w-full border card border-agi-100 card-grid" data-datatable="false" data-datatable-page-size="10"
data-datatable-state-save="false" id="laporan-slik-table"
data-api-url="{{ route('admin-kredit.laporan-slik.datatables') }}">
<div class="flex-wrap py-5 card-header bg-agi-50">
<h3 class="card-title">
Laporan SLIK
</h3>
<div class="flex flex-wrap gap-2 lg:gap-5">
<div class="flex">
<label class="input input-sm">
<i class="ki-filled ki-magnifier"></i>
<input placeholder="Search Laporan SLIK" id="search" type="text" value="">
</label>
</div>
<div class="flex flex-wrap gap-2.5">
<div class="h-[24px] border border-r-gray-200"></div>
<a class="btn btn-sm btn-light" href="#" id="export-excel">
<i class="ki-filled ki-file-down"></i> Export Excel
</a>
</div>
</div>
</div>
<div class="card-body">
<div class="scrollable-x-auto">
<table
class="table text-sm font-medium text-gray-700 align-middle table-auto table-border min-w-[1200px]"
data-datatable-table="true">
<thead>
<tr>
<th class="w-14">
<input class="checkbox checkbox-sm" data-datatable-check="true" type="checkbox" />
</th>
<th class="min-w-[100px]" data-datatable-column="sandi_bank">
<span class="sort">
<span class="sort-label">Sandi Bank</span>
<span class="sort-icon"></span>
</span>
</th>
<th class="min-w-[120px]" data-datatable-column="no_rekening">
<span class="sort">
<span class="sort-label">No Rekening</span>
<span class="sort-icon"></span>
</span>
</th>
<th class="min-w-[120px]" data-datatable-column="baki_debet">
<span class="sort">
<span class="sort-label">Baki Debet</span>
<span class="sort-icon"></span>
</span>
</th>
<th class="min-w-[100px]" data-datatable-column="status">
<span class="sort">
<span class="sort-label">Status</span>
<span class="sort-icon"></span>
</span>
</th>
<th class="min-w-[50px] text-center" data-datatable-column="actions">Aksi</th>
</tr>
</thead>
</table>
</div>
<div
class="flex-col gap-3 justify-center font-medium text-gray-600 card-footer md:justify-between md:flex-row text-2sm">
<div class="flex gap-2 items-center">
Show
<select class="w-16 select select-sm" data-datatable-size="true" name="perpage"> </select> per page
</div>
<div class="flex gap-4 items-center">
<span data-datatable-info="true"> </span>
<div class="pagination" data-datatable-pagination="true">
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
@push('styles')
<style>
/* Responsive adjustments for table */
@media (max-width: 768px) {
.table-responsive {
overflow-x: auto;
}
.modal-content {
max-width: 95% !important;
margin: 10px;
}
.grid-cols-1.md\:grid-cols-2 {
grid-template-columns: 1fr;
}
}
/* Ensure table is scrollable on small screens */
.scrollable-x-auto {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
/* Prevent text wrapping in table cells */
.table th,
.table td {
white-space: nowrap;
vertical-align: middle;
}
/* Allow wrapping for long text in detail modal */
.modal-body .text-gray-900 {
word-break: break-word;
}
</style>
@endpush
@push('scripts')
<script>
let dataTable;
</script>
<script type="module">
/**
* Inisialisasi DataTable untuk Laporan SLIK menggunakan KTDataTable
*/
document.addEventListener('DOMContentLoaded', function() {
const element = document.querySelector('#laporan-slik-table');
const searchInput = document.getElementById('search');
const apiUrl = element.getAttribute('data-api-url');
// Konfigurasi DataTable menggunakan KTDataTable
const dataTableOptions = {
apiEndpoint: apiUrl,
pageSize: 10,
columns: {
select: {
render: (item, data, context) => {
const checkbox = document.createElement('input');
checkbox.className = 'checkbox checkbox-sm';
checkbox.type = 'checkbox';
checkbox.value = data.id.toString();
checkbox.setAttribute('data-datatable-row-check', 'true');
return checkbox.outerHTML.trim();
},
},
sandi_bank: {
title: 'Sandi Bank',
},
no_rekening: {
title: 'No Rekening',
},
baki_debet_formatted: {
title: 'Baki Debet',
render: (item, data) => {
return data.baki_debet_formatted || '-';
},
},
status_badge: {
title: 'Status',
render: (item, data) => {
return data.status_badge || '-';
},
},
actions: {
title: 'Aksi',
render: (item, data) => {
return `
<div class="flex gap-2">
<button class="btn btn-sm btn-light btn-icon" onclick="showDetail(${data.id})" title="Detail">
<i class="ki-filled ki-eye"></i>
</button>
</div>
`;
},
}
},
};
// Inisialisasi DataTable
dataTable = new KTDataTable(element, dataTableOptions);
dataTable.showSpinner();
// Fungsi pencarian
searchInput.addEventListener('input', function() {
const searchValue = this.value.trim();
dataTable.search(searchValue, true);
});
// Export Excel functionality
document.getElementById('export-excel').addEventListener('click', function(e) {
e.preventDefault();
// Build export URL with current filters
const params = new URLSearchParams();
if (searchInput.value) params.append('search', searchInput.value);
const exportUrl = '{{ route('admin-kredit.laporan-slik.export') }}?' + params.toString();
window.open(exportUrl, '_blank');
});
});
</script>
<script>
/**
* Fungsi untuk menampilkan detail data Laporan SLIK
* @param {number} id - ID data Laporan SLIK yang akan ditampilkan
*/
function showDetail(id) {
// Redirect ke halaman detail
window.location.href = `{{ route('admin-kredit.laporan-slik.show', ':id') }}`.replace(':id', id);
}
</script>
@endpush

View File

@@ -0,0 +1,172 @@
@extends('layouts.main')
@section('breadcrumbs')
{{ Breadcrumbs::render('admin-kredit.laporan-slik.show', $laporanSlik) }}
@endsection
@section('content')
<div class="grid">
<div class="w-full">
<!-- Header -->
<div class="flex flex-wrap gap-4 justify-between items-center mb-6">
<div>
<h3 class="text-xl font-semibold text-gray-900">Detail Laporan SLIK</h3>
<p class="mt-1 text-sm text-gray-600">Informasi lengkap laporan SLIK {{ $laporanSlik->nama_debitur }}
</p>
</div>
<div class="flex gap-2">
<a href="{{ route('admin-kredit.laporan-slik.index') }}" class="btn btn-sm btn-light">
<i class="ki-filled ki-arrow-left"></i>
Kembali
</a>
</div>
</div>
<!-- Card Detail -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Informasi Laporan SLIK</h3>
</div>
<div class="card-body">
<div class="grid grid-cols-1 gap-6 md:grid-cols-2">
<!-- Informasi Debitur -->
<div class="space-y-4">
<h4 class="mb-3 text-base font-semibold text-gray-800">Data Debitur</h4>
<div class="space-y-3">
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">Nama Debitur:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->nama_debitur ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">No. Rekening:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->no_rekening ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">CIF:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->cif ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">NPWP:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->npwp ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">No. KTP:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->no_ktp ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">No. Telp:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->no_telp ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">Alamat:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->alamat ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">Kode Pos:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->kode_pos ?? '-' }}</span>
</div>
</div>
</div>
<!-- Informasi Fasilitas -->
<div class="space-y-4">
<h4 class="mb-3 text-base font-semibold text-gray-800">Data Fasilitas</h4>
<div class="space-y-3">
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">Fasilitas:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->fasilitas ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">Kolektibilitas:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->kolektibilitas ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">Jenis Agunan:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->jenis_agunan ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">Nilai Agunan:</span>
<span class="text-sm text-gray-900">Rp
{{ number_format($laporanSlik->nilai_agunan ?? 0, 0, ',', '.') }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">Baki Debet:</span>
<span class="text-sm text-gray-900">Rp
{{ number_format($laporanSlik->baki_debet ?? 0, 0, ',', '.') }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">Sandi Bank:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->sandi_bank ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">Status:</span>
<span class="text-sm text-gray-900">
<span
class="badge badge-light-{{ $laporanSlik->status == 'aktif' ? 'success' : 'danger' }}">
{{ $laporanSlik->status ?? 'aktif' }}
</span>
</span>
</div>
</div>
</div>
</div>
<div class="mt-4 row">
<div class="col-12">
<h5 class="mb-3">Detail Lainnya</h5>
<table class="table table-borderless">
<tr>
<td width="20%"><strong>Kolektibilitas</strong></td>
<td width="5%">:</td>
<td>{{ $laporanSlik->kolektibilitas }}</td>
<td width="20%"><strong>Tanggal Mulai</strong></td>
<td width="5%">:</td>
<td>{{ $laporanSlik->tanggal_mulai ?? '-' }}</td>
</tr>
<tr>
<td><strong>Tanggal Jatuh Tempo</strong></td>
<td>:</td>
<td>{{ $laporanSlik->tanggal_jatuh_tempo ?? '-' }}</td>
<td><strong>Tanggal Selesai</strong></td>
<td>:</td>
<td>{{ $laporanSlik->tanggal_selesai ?? '-' }}</td>
</tr>
<tr>
<td><strong>Jenis Agunan</strong></td>
<td>:</td>
<td>{{ $laporanSlik->jenis_agunan ?? '-' }}</td>
<td><strong>Fasilitas</strong></td>
<td>:</td>
<td>{{ $laporanSlik->fasilitas ?? '-' }}</td>
</tr>
<tr>
<td><strong>Tanggal Lapor</strong></td>
<td>:</td>
<td>{{ $laporanSlik->tanggal_lapor ?? '-' }}</td>
<td><strong>Tanggal Dibuat</strong></td>
<td>:</td>
<td>{{ $laporanSlik->created_at->format('d/m/Y H:i') }}</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection