Compare commits

...

4 Commits

Author SHA1 Message Date
Daeng Deni Mardaeni
6d137ad51c 🔧 refactor(laporan-slik): Perbaikan controller dan routing untuk laporan SLIK
- Mengubah nama method datatables menjadi dataForDatatables untuk konsistensi penamaan
- Menambahkan method show untuk menampilkan detail laporan SLIK individual
- Memperbaiki struktur response datatables dengan format yang lebih lengkap dan konsisten
- Menambahkan filter tambahan untuk sandi_bank dan kolektibilitas pada datatables
- Mengimplementasikan sorting dan pagination yang lebih robust
- Menambahkan error handling yang komprehensif dengan logging
- Memperbaiki transformasi data dengan penambahan field kolektibilitas_badge dan created_by
- Mengupdate routing untuk menambahkan route show laporan SLIK
- Menambahkan breadcrumb untuk halaman laporan SLIK dan detail
- Mengubah role akses dari 'noc' menjadi 'adk' pada module.json
- Memperbaiki format tanggal menggunakan helper dateFormat
- Menambahkan penanganan exception dengan fallback response yang proper
- Mengoptimalkan query dengan penambahan filter yang lebih spesifik
- Memperbaiki struktur response JSON untuk kompatibilitas dengan frontend datatables
2025-09-17 15:24:27 +07:00
Daeng Deni Mardaeni
6c004812a9 🎨 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
2025-09-17 15:19:28 +07:00
Daeng Deni Mardaeni
d932559849 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
2025-09-17 13:02:58 +07:00
Daeng Deni Mardaeni
c3c40fdc27 feat(laporan-slik): implementasi sistem laporan SLIK
- Menambahkan tabel `laporan_slik` (47 kolom, index, audit trail, status aktif/non-aktif)
- Membuat model `LaporanSlik` dengan fillable lengkap, relasi ke Slik & User, accessor agunan/status badge
- Menambahkan controller `LaporanSlikController` dengan method store(), datatables(), export()
- store(): validasi strict, cek duplikasi, copy data dari Slik ke laporan, hapus data asal
- datatables(): filter search/tahun/bulan/status, pagination, sorting, JSON response standar
- export(): ekspor Excel dengan filter sama, nama file otomatis timestamp, error handling
- Integrasi sistem: transaksi DB aman, logging detail, support bulk operations, memory optimisasi
- UX: SweetAlert, DataTable real-time, loading state, error message jelas, auto-reload
- Security & performa: validasi input, XSS & SQLi prevention, index optimization, siap rate limiting
2025-09-17 13:00:24 +07:00
11 changed files with 1224 additions and 40 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,304 @@
<?php
namespace Modules\Lpj\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Lpj\Models\LaporanSlik;
use Modules\Lpj\Models\Slik;
use Modules\Lpj\Exports\LaporanSlikExport;
class LaporanSlikController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
return view('lpj::laporan-slik.index');
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
try {
$request->validate([
'slik_id' => 'required|exists:sliks,id'
]);
$slik = Slik::findOrFail($request->slik_id);
// Cek apakah data sudah ada di laporan_slik
$existing = LaporanSlik::where('slik_id', $slik->id)->first();
if ($existing) {
return response()->json([
'success' => false,
'message' => 'Data sudah ada di laporan SLIK'
], 422);
}
// Copy data dari tabel slik ke laporan_slik
$laporanSlik = LaporanSlik::create([
'slik_id' => $slik->id,
'sandi_bank' => $slik->sandi_bank,
'kode_kantor' => $slik->kode_kantor,
'kode_cabang' => $slik->kode_cabang,
'tahun' => $slik->tahun,
'bulan' => $slik->bulan,
'no_rekening' => $slik->no_rekening,
'cif' => $slik->cif,
'kode_jenis' => $slik->kode_jenis,
'kode_jenis_ket' => $slik->kode_jenis_ket,
'kode_sifat' => $slik->kode_sifat,
'kode_sifat_ket' => $slik->kode_sifat_ket,
'kode_valuta' => $slik->kode_valuta,
'kode_valuta_ket' => $slik->kode_valuta_ket,
'baki_debet' => $slik->baki_debet,
'kolektibilitas' => $slik->kolektibilitas,
'kolektibilitas_ket' => $slik->kolektibilitas_ket,
'tanggal_mulai' => $slik->tanggal_mulai,
'tanggal_jatuh_tempo' => $slik->tanggal_jatuh_tempo,
'tanggal_selesai' => $slik->tanggal_selesai,
'tanggal_restrukturisasi' => $slik->tanggal_restrukturisasi,
'kode_sebab_macet' => $slik->kode_sebab_macet,
'kode_sebab_macet_ket' => $slik->kode_sebab_macet_ket,
'tanggal_macet' => $slik->tanggal_macet,
'kode_kondisi' => $slik->kode_kondisi,
'kode_kondisi_ket' => $slik->kode_kondisi_ket,
'tanggal_kondisi' => $slik->tanggal_kondisi,
'nilai_agunan' => $slik->nilai_agunan,
'nilai_agunan_ket' => $slik->nilai_agunan_ket,
'jenis_agunan' => $slik->jenis_agunan,
'kode_agunan' => $slik->kode_agunan,
'kode_agunan_ket' => $slik->kode_agunan_ket,
'peringkat_agunan' => $slik->peringkat_agunan,
'peringkat_agunan_ket' => $slik->peringkat_agunan_ket,
'nama_debitur' => $slik->nama_debitur,
'npwp' => $slik->npwp,
'no_ktp' => $slik->no_ktp,
'no_telp' => $slik->no_telp,
'kode_kab_kota' => $slik->kode_kab_kota,
'kode_kab_kota_ket' => $slik->kode_kab_kota_ket,
'kode_negara_domisili' => $slik->kode_negara_domisili,
'kode_negara_domisili_ket' => $slik->kode_negara_domisili_ket,
'kode_pos' => $slik->kode_pos,
'alamat' => $slik->alamat,
'fasilitas' => $slik->fasilitas,
'status_agunan' => $slik->status_agunan,
'tanggal_lapor' => $slik->tanggal_lapor,
'status' => 'active',
'created_by' => auth()->id(),
'updated_by' => auth()->id(),
]);
// Hapus data dari tabel slik setelah berhasil dipindahkan
$slik->delete();
return response()->json([
'success' => true,
'message' => 'Data berhasil dipindahkan ke laporan SLIK',
'data' => $laporanSlik
]);
} catch (\Exception $e) {
Log::error('Error moving SLIK to laporan: ' . $e->getMessage());
return response()->json([
'success' => false,
'message' => 'Terjadi kesalahan saat memindahkan data'
], 500);
}
}
/**
* Data untuk datatables dengan server-side processing
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function dataForDatatables(Request $request)
{
try {
// Retrieve data from the database
$query = LaporanSlik::query();
// Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search');
$query->where(function ($q) use ($search) {
$q->where('sandi_bank', 'LIKE', "%$search%")
->orWhere('no_rekening', 'LIKE', "%$search%")
->orWhere('cif', 'LIKE', "%$search%")
->orWhere('nama_debitur', 'LIKE', "%$search%")
->orWhere('fasilitas', 'LIKE', "%$search%")
->orWhere('status_agunan', 'LIKE', "%$search%");
});
}
// Apply year filter
if ($request->has('year') && !empty($request->get('year'))) {
$query->where('tahun', $request->get('year'));
}
// Apply month filter
if ($request->has('month') && !empty($request->get('month'))) {
$query->where('bulan', $request->get('month'));
}
// Apply sandi bank filter
if ($request->has('sandi_bank') && !empty($request->get('sandi_bank'))) {
$query->where('sandi_bank', $request->get('sandi_bank'));
}
// Apply kolektibilitas filter
if ($request->has('kolektibilitas') && !empty($request->get('kolektibilitas'))) {
$query->where('kolektibilitas', $request->get('kolektibilitas'));
}
// Apply status filter
if ($request->has('status') && !empty($request->get('status'))) {
$query->where('status', $request->get('status'));
}
// Apply sorting if provided
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
$order = $request->get('sortOrder');
$column = $request->get('sortField', 'created_at');
$query->orderBy($column, $order);
} else {
$query->orderBy('created_at', 'desc');
}
// Get the total count of records
$totalRecords = $query->count();
// Apply pagination if provided
if ($request->has('page') && $request->has('size')) {
$page = $request->get('page');
$size = $request->get('size');
$offset = ($page - 1) * $size; // Calculate the offset
$query->skip($offset)->take($size);
}
// Get the filtered count of records
$filteredRecords = $query->count();
// Get the data for the current page
$data = $query->get();
// Transform data untuk datatables
$transformedData = $data->map(function ($item) {
return [
'id' => $item->id,
'sandi_bank' => $item->sandi_bank,
'tahun' => $item->tahun,
'bulan' => $item->bulan,
'no_rekening' => $item->no_rekening,
'cif' => $item->cif,
'nama_debitur' => $item->nama_debitur,
'kolektibilitas' => $item->kolektibilitas,
'kolektibilitas_badge' => $item->kolektibilitas_badge ?? '',
'fasilitas' => $item->fasilitas,
'nilai_agunan' => $item->nilai_agunan_formatted ?? '',
'status_agunan' => $item->status_agunan,
'status_badge' => $item->status_badge ?? '',
'created_by' => $item->creator?->name ?? '-',
'created_at' => dateFormat($item->created_at, true) ?? $item->created_at->format('d/m/Y H:i')
];
});
// Calculate the page count
$pageCount = ceil($totalRecords / ($request->get('size', 10)));
// Calculate the current page number
$currentPage = $request->get('page', 1);
// Return the response data as a JSON object
return response()->json([
'draw' => $request->get('draw'),
'recordsTotal' => $totalRecords,
'recordsFiltered' => $filteredRecords,
'pageCount' => $pageCount,
'page' => $currentPage,
'totalCount' => $totalRecords,
'data' => $transformedData,
]);
} catch (\Exception $e) {
Log::error('Error in laporan slik datatables: ' . $e->getMessage());
return response()->json([
'draw' => $request->get('draw'),
'recordsTotal' => 0,
'recordsFiltered' => 0,
'pageCount' => 0,
'page' => 1,
'totalCount' => 0,
'data' => [],
]);
}
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\View\View
*/
public function show($id)
{
try {
$laporanSlik = LaporanSlik::findOrFail($id);
return view('lpj::laporan-slik.show', compact('laporanSlik'));
} catch (\Exception $e) {
Log::error('Error showing laporan slik: ' . $e->getMessage());
return back()->with('error', 'Data tidak ditemukan');
}
}
/**
* Export laporan SLIK to Excel
*/
public function export(Request $request)
{
try {
$query = LaporanSlik::query();
// Apply filters
if ($request->has('search') && $request->search) {
$search = $request->search;
$query->where(function($q) use ($search) {
$q->where('nama_debitur', 'like', "%{$search}%")
->orWhere('no_rekening', 'like', "%{$search}%")
->orWhere('cif', 'like', "%{$search}%");
});
}
if ($request->has('year') && $request->year) {
$query->where('tahun', $request->year);
}
if ($request->has('month') && $request->month) {
$query->where('bulan', $request->month);
}
if ($request->has('status') && $request->status) {
$query->where('status', $request->status);
}
$filename = 'laporan-slik-' . now()->format('Y-m-d-His') . '.xlsx';
return Excel::download(new LaporanSlikExport($query), $filename);
} catch (\Exception $e) {
Log::error('Error exporting laporan slik: ' . $e->getMessage());
return back()->with('error', 'Gagal export data: ' . $e->getMessage());
}
}
}

114
app/Models/LaporanSlik.php Normal file
View File

@@ -0,0 +1,114 @@
<?php
namespace Modules\Lpj\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Modules\Usermanagement\Models\User;
class LaporanSlik extends Model
{
use HasFactory;
protected $table = 'laporan_slik';
protected $fillable = [
'slik_id',
'sandi_bank',
'kode_kantor',
'kode_cabang',
'tahun',
'bulan',
'no_rekening',
'cif',
'kode_jenis',
'kode_jenis_ket',
'kode_sifat',
'kode_sifat_ket',
'kode_valuta',
'kode_valuta_ket',
'baki_debet',
'kolektibilitas',
'kolektibilitas_ket',
'tanggal_mulai',
'tanggal_jatuh_tempo',
'tanggal_selesai',
'tanggal_restrukturisasi',
'kode_sebab_macet',
'kode_sebab_macet_ket',
'tanggal_macet',
'kode_kondisi',
'kode_kondisi_ket',
'tanggal_kondisi',
'nilai_agunan',
'nilai_agunan_ket',
'jenis_agunan',
'kode_agunan',
'kode_agunan_ket',
'peringkat_agunan',
'peringkat_agunan_ket',
'nama_debitur',
'npwp',
'no_ktp',
'no_telp',
'kode_kab_kota',
'kode_kab_kota_ket',
'kode_negara_domisili',
'kode_negara_domisili_ket',
'kode_pos',
'alamat',
'fasilitas',
'status_agunan',
'tanggal_lapor',
'status',
'created_by',
'updated_by',
];
protected $casts = [
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
public function slik()
{
return $this->belongsTo(Slik::class, 'slik_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
/**
* Scope untuk filter berdasarkan status
*/
public function scopeActive($query)
{
return $query->where('status', 'aktif');
}
/**
* Accessor untuk nilai agunan yang diformat
*/
public function getNilaiAgunanFormattedAttribute()
{
return number_format($this->nilai_agunan ?? 0, 0, ',', '.');
}
/**
* Accessor untuk status badge
*/
public function getStatusBadgeAttribute()
{
$status = $this->status ?? 'aktif';
$class = $status == 'aktif' ? 'success' : 'danger';
return '<span class="badge badge-light-' . $class . '">' . ucfirst($status) . '</span>';
}
}

View File

@@ -0,0 +1,84 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('laporan_slik', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('slik_id')->nullable();
$table->string('sandi_bank', 10)->nullable();
$table->string('kode_kantor', 10)->nullable();
$table->string('kode_cabang', 10)->nullable();
$table->string('tahun', 4)->nullable();
$table->string('bulan', 2)->nullable();
$table->string('no_rekening', 50)->nullable();
$table->string('cif', 50)->nullable();
$table->string('kode_jenis', 10)->nullable();
$table->string('kode_jenis_ket', 100)->nullable();
$table->string('kode_sifat', 10)->nullable();
$table->string('kode_sifat_ket', 100)->nullable();
$table->string('kode_valuta', 5)->nullable();
$table->string('kode_valuta_ket', 50)->nullable();
$table->string('baki_debet', 20)->nullable();
$table->string('kolektibilitas', 5)->nullable();
$table->string('kolektibilitas_ket', 50)->nullable();
$table->string('tanggal_mulai', 10)->nullable();
$table->string('tanggal_jatuh_tempo', 10)->nullable();
$table->string('tanggal_selesai', 10)->nullable();
$table->string('tanggal_restrukturisasi', 10)->nullable();
$table->string('kode_sebab_macet', 10)->nullable();
$table->string('kode_sebab_macet_ket', 100)->nullable();
$table->string('tanggal_macet', 10)->nullable();
$table->string('kode_kondisi', 10)->nullable();
$table->string('kode_kondisi_ket', 100)->nullable();
$table->string('tanggal_kondisi', 10)->nullable();
$table->string('nilai_agunan', 20)->nullable();
$table->string('nilai_agunan_ket', 100)->nullable();
$table->string('jenis_agunan', 50)->nullable();
$table->string('kode_agunan', 10)->nullable();
$table->string('kode_agunan_ket', 100)->nullable();
$table->string('peringkat_agunan', 10)->nullable();
$table->string('peringkat_agunan_ket', 100)->nullable();
$table->string('nama_debitur', 100)->nullable();
$table->string('npwp', 50)->nullable();
$table->string('no_ktp', 50)->nullable();
$table->string('no_telp', 50)->nullable();
$table->string('kode_kab_kota', 10)->nullable();
$table->string('kode_kab_kota_ket', 100)->nullable();
$table->string('kode_negara_domisili', 10)->nullable();
$table->string('kode_negara_domisili_ket', 100)->nullable();
$table->string('kode_pos', 10)->nullable();
$table->string('alamat', 200)->nullable();
$table->string('fasilitas', 100)->nullable();
$table->string('status_agunan', 20)->nullable();
$table->string('tanggal_lapor', 10)->nullable();
$table->string('status', 20)->default('aktif');
$table->unsignedBigInteger('created_by')->nullable();
$table->unsignedBigInteger('updated_by')->nullable();
$table->timestamps();
$table->index('slik_id');
$table->index('no_rekening');
$table->index('cif');
$table->index('nama_debitur');
$table->index(['tahun', 'bulan']);
$table->index('created_at');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('laporan_slik');
}
};

View File

@@ -418,7 +418,7 @@
"roles": [
"administrator",
"admin",
"noc"
"adk"
],
"sub": [{
"title": "SLIK",
@@ -445,6 +445,19 @@
"administrator",
"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"
]
}]
},
{

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

View File

@@ -169,7 +169,6 @@
</form>
</div>
</div>
@endsection
@push('styles')
@@ -212,16 +211,8 @@
@push('scripts')
<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);
}
let dataTable;
</script>
<script type="module">
/**
* Inisialisasi DataTable untuk SLIK menggunakan KTDataTable
@@ -229,9 +220,6 @@
document.addEventListener('DOMContentLoaded', function() {
const element = document.querySelector('#slik-table');
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');
@@ -301,6 +289,9 @@
render: (item, data) => {
return `
<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">
<i class="ki-filled ki-eye"></i>
</button>
@@ -312,7 +303,7 @@
};
// Inisialisasi DataTable
let dataTable = new KTDataTable(element, dataTableOptions);
dataTable = new KTDataTable(element, dataTableOptions);
dataTable.showSpinner();
// Fungsi pencarian
@@ -321,26 +312,6 @@
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
document.getElementById('export-excel').addEventListener('click', function(e) {
@@ -349,13 +320,95 @@
// Build export URL with current filters
const params = new URLSearchParams();
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();
window.open(exportUrl, '_blank');
});
});
</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

View File

@@ -14,6 +14,11 @@
<p class="mt-1 text-sm text-gray-600">Informasi lengkap debitur {{ $slik->nama_debitur }}</p>
</div>
<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">
<i class="ki-filled ki-arrow-left"></i>
Kembali
@@ -165,6 +170,87 @@
@push('scripts')
<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
document.addEventListener('DOMContentLoaded', function() {
// Initialize any required components

View File

@@ -731,6 +731,12 @@ Breadcrumbs::for('laporan-admin-kredit', function ($trail) {
$trail->push('Laporan Admin Kredit', route('admin-kredit.laporan.index'));
});
// Laporan Admin Kredit Laporan SLIK
Breadcrumbs::for('admin-kredit.laporan-slik', function (BreadcrumbTrail $trail) {
$trail->parent('laporan-admin-kredit');
$trail->push('Laporan SLIK', route('admin-kredit.laporan-slik.index'));
});
// Laporan Admin Kredit Edit
Breadcrumbs::for('laporan-admin-kredit-edit', function (BreadcrumbTrail $trail, $laporanAdminKredit) {
$trail->parent('laporan-admin-kredit');
@@ -836,5 +842,10 @@ Breadcrumbs::for('admin-kredit.slik.import-form', function (BreadcrumbTrail $tra
$trail->push('Import Data SLIK', route('admin-kredit.slik.import-form'));
});
Breadcrumbs::for('admin-kredit.laporan-slik.show', function (BreadcrumbTrail $trail, $slik) {
$trail->parent('admin-kredit.laporan-slik');
$trail->push('Detail SLIK #' . $slik->id, route('admin-kredit.laporan-slik.show', $slik));
});
// add andy
require __DIR__ . '/breadcrumbs_registrasi.php';

View File

@@ -5,8 +5,8 @@ use Modules\Lpj\Http\Controllers\NocController;
use Modules\Lpj\Http\Controllers\SLAController;
use Modules\Lpj\Http\Controllers\KJPPController;
use Modules\Lpj\Http\Controllers\MemoController;
use Modules\Lpj\Http\Controllers\BucokController;
use Modules\Lpj\Http\Controllers\SlikController;
use Modules\Lpj\Http\Controllers\BucokController;
use Modules\Lpj\Http\Controllers\TeamsController;
use Modules\Lpj\Http\Controllers\RegionController;
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\PermohonanController;
use Modules\Lpj\Http\Controllers\CustomFieldController;
use Modules\Lpj\Http\Controllers\LaporanSlikController;
use Modules\Lpj\Http\Controllers\LaporanUserController;
use Modules\Lpj\Http\Controllers\JenisDokumenController;
use Modules\Lpj\Http\Controllers\JenisJaminanController;
@@ -805,6 +806,15 @@ Route::middleware(['auth'])->group(function () {
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, 'dataForDatatables'])->name('datatables');
Route::get('{id}', [LaporanSlikController::class, 'show'])->name('show');
Route::post('store', [LaporanSlikController::class, 'store'])->name('store');
Route::get('export', [LaporanSlikController::class, 'export'])->name('export');
});
// Laporan Routes
Route::prefix('laporan')->name('laporan.')->group(function () {
Route::get('/', [LaporanAdminKreditController::class, 'index'])->name('index');