feat(memo): tambah tombol download PDF dan disable checkbox untuk memo selesai

- Menambahkan field memo_penyelesaian_pdf_path ke tabel noc untuk menyimpan path file PDF
- Membuat migrasi baru untuk menambahkan field PDF path ke tabel noc
- Menambahkan field memo_penyelesaian_pdf_path ke model Noc dalam fillable array
- Memodifikasi fungsi generatePdf di MemoController untuk menyimpan path PDF ke database
- Menambahkan route baru memo.download-pdf untuk download file PDF memo penyelesaian
- Membuat method downloadPdf di MemoController dengan validasi file dan error handling
- Memodifikasi kolom select di datatables untuk disable checkbox jika sudah ada memo
- Menambahkan tooltip pada checkbox yang disabled untuk memberikan informasi kepada user
- Memodifikasi kolom actions untuk menampilkan tombol download PDF jika memo sudah ada
- Menampilkan informasi nomor memo dan tanggal memo di kolom actions
- Memodifikasi fungsi handleCheckboxChange untuk mengabaikan checkbox yang disabled
- Menambahkan styling untuk tombol download dengan icon dan warna yang sesuai
- Menambahkan logging untuk tracking aktivitas download PDF memo penyelesaian
- Menambahkan validasi keberadaan file di storage sebelum mengizinkan download
- Menggunakan Storage facade untuk operasi file yang lebih aman dan konsisten
This commit is contained in:
Daeng Deni Mardaeni
2025-07-17 13:47:53 +07:00
parent 57dece449c
commit 4459b70271
4 changed files with 77 additions and 13 deletions

View File

@@ -241,6 +241,7 @@ class MemoController extends Controller
'debiture',
'branch',
'tujuanPenilaian',
'jenisPenilaian',
'penilaian',
'jenisFasilitasKredit',
'documents.inspeksi',
@@ -516,4 +517,41 @@ class MemoController extends Controller
$uniqueJaminan = array_unique($jaminanTypes);
return implode(' & ', $uniqueJaminan);
}
/**
* Download PDF memo penyelesaian
*
* @param int $id - ID permohonan
* @return \Illuminate\Http\Response
*/
public function downloadPdf($id)
{
Log::info('MemoController: Download PDF memo penyelesaian untuk permohonan ID: ' . $id);
try {
// Cari NOC berdasarkan permohonan_id
$noc = Noc::where('permohonan_id', $id)->first();
if (!$noc || !$noc->memo_penyelesaian) {
Log::warning('MemoController: PDF memo penyelesaian tidak ditemukan untuk permohonan ID: ' . $id);
return redirect()->back()->with('error', 'File PDF memo penyelesaian tidak ditemukan.');
}
// Cek apakah file ada di storage
if (!Storage::disk('public')->exists($noc->memo_penyelesaian)) {
Log::warning('MemoController: File PDF tidak ada di storage: ' . $noc->memo_penyelesaian);
return redirect()->back()->with('error', 'File PDF tidak ditemukan di server.');
}
// Download file
$fileName = 'memo-penyelesaian-' . $noc->memo_penyelesaian_number . '.pdf';
Log::info('MemoController: Berhasil download PDF memo penyelesaian: ' . $fileName);
return Storage::disk('public')->download($noc->memo_penyelesaian, $fileName);
} catch (Exception $e) {
Log::error('MemoController: Error saat download PDF memo penyelesaian - ' . $e->getMessage());
return redirect()->back()->with('error', 'Terjadi kesalahan saat mengunduh file PDF.');
}
}
}

View File

@@ -239,16 +239,6 @@
let resumeButton = '';
let penyelesaian = '';
if (data.noc) {
if (!data.noc?.memo_penyelesaian) {
penyelesaian = `
<a href="{{ route('noc.index') }}/${data.noc.id}" class="btn btn-sm btn-warning">
Penyelesaian
</a>`;
}
}
if (data.penilai.resume) {
resumeButton = `
<a href="{{ route('penilai.print-out') }}?permohonanId=${data.id}&documentId=${dokumenID}&inspeksiId=${inspeksiId}&jaminanId=${jenisJaminanID}&statusLpj=0" class="btn btn-sm btn-success">

View File

@@ -54,6 +54,10 @@
<span class="sort"> <span class="sort-label"> AO </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="jenis_penilaian_id">
<span class="sort"> <span class="sort-label"> Jenis Penilaian </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="tujuan_penilaian_id">
<span class="sort"> <span class="sort-label"> Tujuan Penilaian </span>
<span class="sort-icon"> </span> </span>
@@ -134,11 +138,21 @@
columns: {
select: {
render: (item, data, context) => {
// Cek apakah sudah ada memo penyelesaian
const hasMemo = data.noc && data.noc.memo_penyelesaian;
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');
// Disable checkbox jika sudah ada memo
if (hasMemo) {
checkbox.disabled = true;
checkbox.title = 'Permohonan ini sudah memiliki memo penyelesaian';
}
return checkbox.outerHTML.trim();
},
},
@@ -166,6 +180,12 @@
return `${data.user.name}`;
},
},
jenis_penilaian_id: {
title: 'Jenis Penilaian',
render: (item, data) => {
return `${data.jenis_penilaian.name}`;
}
},
tujuan_penilaian_id: {
title: 'Tujuan Penilaian',
render: (item, data) => {
@@ -252,9 +272,21 @@
render: (item, data) => {
let actionButtons = '';
actionButtons = `
// Cek apakah sudah ada memo penyelesaian dengan PDF
if (data.noc && data.noc.memo_penyelesaian) {
actionButtons = `
<div class="flex flex-col gap-1">
<a href="memo/memo/download-pdf/${data.id}"
class="flex gap-1 items-center btn btn-sm btn-primary"
title="Download PDF Memo Penyelesaian">
<i class="fas fa-download"></i>
Download Memo
</a>
</div>`;
} else {
actionButtons = `
<span class="text-sm text-gray-500">Belum ada memo</span>`;
}
return `<div class="flex flex-wrap gap-1.5 justify-center">${actionButtons}</div>`;
},
@@ -268,7 +300,8 @@
* Fungsi untuk menangani perubahan checkbox
*/
function handleCheckboxChange() {
const checkboxes = document.querySelectorAll('input[data-datatable-row-check="true"]:checked');
// Hanya ambil checkbox yang tidak disabled dan checked
const checkboxes = document.querySelectorAll('input[data-datatable-row-check="true"]:checked:not(:disabled)');
selectedItems = Array.from(checkboxes).map(cb => cb.value);
// Enable/disable tombol create memo berdasarkan jumlah item yang dipilih

View File

@@ -404,6 +404,9 @@ Route::middleware(['auth'])->group(function () {
Route::post('total-biaya-pj', [MemoController::class, 'getTotalBiayaPJ'])->name('memo.total-biaya-pj');
Route::post('preview', [MemoController::class, 'preview'])->name('memo.preview');
Route::post('generate-pdf', [MemoController::class, 'generatePdf'])->name('memo.generate-pdf');
// Route untuk download PDF memo penyelesaian
Route::get('/memo/download-pdf/{id}', [MemoController::class, 'downloadPdf'])
->name('memo.download-pdf');
});
Route::resource('memo', MemoController::class);