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:
@@ -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.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user