Files
lpj/resources/views/memo/create.blade.php
Daeng Deni Mardaeni cf0059fe66 feat(memo): implementasi jenis penilaian dinamis dan perbaikan checkbox pada memo penyelesaian
Perubahan yang dilakukan:
- Menghapus validasi input pada method `preview()` untuk mendukung fleksibilitas data preview
- Mengganti eager loading dari `tujuanPenilaian` menjadi `jenisPenilaian` agar sesuai dengan kebutuhan data dinamis
- Menambahkan method chaining `->get()` pada akhir query untuk memastikan eksekusi query yang benar
- Menambahkan field `jenisPenilaian` ke dalam memoData agar template dapat menampilkan instruksi pembayaran secara dinamis
- Mengimplementasikan checkbox visual yang disabled dengan hidden input untuk tetap mengirim data saat form submit
- Memisahkan antara checkbox untuk tampilan (disabled) dan input data (hidden) untuk meningkatkan UX
- Mengubah lebar label dari 80px menjadi 200px pada template PDF dan preview untuk layout yang lebih baik
- Mengganti informasi jaminan menjadi statis "Tanah & Bangunan" pada template PDF dan preview
- Menambahkan conditional rendering untuk menampilkan instruksi pembayaran sesuai dengan jenis penilaian (Internal/KJPP)
- Menyesuaikan layout dan formatting pada template PDF dan preview agar lebih konsisten secara visual
- Menambahkan logika text dinamis untuk jenis penilaian pada bagian instruksi pembayaran
- Mengoptimalkan struktur query agar lebih efisien dan menghindari duplikasi

Tujuan perubahan:
- Mendukung proses memo penyelesaian dengan jenis penilaian yang lebih fleksibel (Internal/KJPP)
- Meningkatkan pengalaman pengguna dengan tampilan checkbox yang jelas namun tetap menyimpan data dengan aman
- Menyederhanakan layout dan formatting agar lebih profesional dan konsisten di preview maupun PDF
- Memastikan proses generate memo berjalan sesuai kebutuhan bisnis dengan instruksi pembayaran yang tepat
2025-07-17 16:05:56 +07:00

307 lines
15 KiB
PHP

@extends('layouts.main')
@section('breadcrumbs')
{{ Breadcrumbs::render('memo.create') }}
@endsection
@section('content')
<div class="grid gap-5 mx-auto w-full lg:gap-7.5">
<!-- Form Memo Penyelesaian -->
<div class="card">
<div class="card-header">
<h3 class="card-title">
Buat Memo Penyelesaian
</h3>
<div class="flex gap-2 items-center">
<a href="{{ route('memo.index') }}" class="btn btn-sm btn-light">
<i class="ki-filled ki-black-left"></i>
Kembali
</a>
</div>
</div>
<div class="card-body">
@if (session('error'))
<div class="alert alert-danger">
{{ session('error') }}
</div>
@endif
<form action="{{ route('memo.preview') }}" method="POST" id="memo-form">
@csrf
<!-- Form Input Memo -->
<div class="grid grid-cols-1 gap-5 mb-7 lg:grid-cols-2">
<div class="flex flex-col gap-1">
<label class="text-gray-900 form-label">Nomor Memo <span class="text-red-500">*</span></label>
<input type="text" name="memo_number" class="input"
placeholder="Masukkan nomor memo penyelesaian" value="{{ old('memo_number') }}" required>
@error('memo_number')
<span class="text-sm text-red-500">{{ $message }}</span>
@enderror
</div>
<div class="flex flex-col gap-1">
<label class="text-gray-900 form-label">Tanggal Pembayaran <span
class="text-red-500">*</span></label>
<input type="date" name="payment_date" class="input" value="{{ old('payment_date') }}"
required>
@error('payment_date')
<span class="text-sm text-red-500">{{ $message }}</span>
@enderror
</div>
<div class="flex flex-col gap-1 lg:col-span-2">
<label class="text-gray-900 form-label">Total Biaya PJ</label>
<div class="relative">
<input type="text" id="total-biaya-pj" class="bg-gray-50 input"
value="Rp {{ number_format($totalBiayaPJ ?? 0, 0, ',', '.') }}" readonly>
<div class="flex absolute inset-y-0 right-0 items-center pr-3">
<i class="text-gray-400 ki-filled ki-calculator"></i>
</div>
</div>
<span class="text-sm text-gray-500">Total biaya dari nominal bayar NOC untuk permohonan yang
dipilih</span>
</div>
</div>
<!-- Hidden field untuk tanggal memo otomatis -->
<input type="hidden" name="memo_date" value="{{ date('Y-m-d') }}">
<!-- Daftar Permohonan yang Dipilih -->
@if (count($permohonanList) > 0)
<div class="mb-7">
<h4 class="mb-4 text-lg font-semibold">Daftar Permohonan yang Akan Diproses</h4>
<div class="overflow-x-auto">
<table class="table table-border">
<thead>
<tr class="bg-gray-50">
<th class="w-12 text-center">
<input type="checkbox" id="select-all" class="checkbox checkbox-sm" checked
disabled>
</th>
<th>Nomor Registrasi</th>
<th>Debitur</th>
<th>Cabang</th>
<th>AO</th>
<th>Tujuan Penilaian</th>
<th>Status</th>
</tr>
</thead>
<tbody>
@foreach ($permohonanList as $permohonan)
<tr>
<td class="text-center">
<input type="checkbox" class="checkbox checkbox-sm permohonan-checkbox"
checked disabled>
<input type="hidden" name="permohonan_ids[]"
value="{{ $permohonan->id }}">
</td>
<td class="font-medium">{{ $permohonan->nomor_registrasi }}</td>
<td>{{ $permohonan->debiture->name ?? '-' }}</td>
<td>{{ $permohonan->branch->name ?? '-' }}</td>
<td>{{ $permohonan->user->name ?? '-' }}</td>
<td>
@if ($permohonan->tujuanPenilaian)
<span class="badge badge-sm badge-primary">
{{ $permohonan->tujuanPenilaian->name }}
</span>
@else
-
@endif
</td>
<td>
<span class="uppercase badge badge-sm badge-warning">
{{ $permohonan->status }}
</span>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div class="p-4 mt-4 bg-blue-50 rounded-lg">
<div class="flex gap-2 items-center">
<i class="text-blue-600 ki-filled ki-information"></i>
<span class="font-medium text-blue-800">Informasi:</span>
</div>
<p class="mt-1 text-blue-700">
Total <span id="selected-count">{{ count($permohonanList) }}</span> permohonan akan
diproses untuk memo penyelesaian.
Anda dapat menghapus centang pada permohonan yang tidak ingin diproses.
</p>
</div>
</div>
<!-- Tombol Submit -->
<div class="flex gap-2 justify-end">
<a href="{{ route('memo.index') }}" class="btn btn-light">
Batal
</a>
<button type="submit" class="btn btn-primary" id="submit-btn">
<i class="ki-filled ki-eye"></i>
Preview Memo Penyelesaian
</button>
</div>
@else
<div class="py-10 text-center">
<div class="mb-4">
<i class="text-6xl text-gray-400 ki-filled ki-information-2"></i>
</div>
<h4 class="mb-2 text-lg font-semibold text-gray-600">Tidak Ada Data</h4>
<p class="mb-4 text-gray-500">Tidak ada permohonan yang dipilih untuk diproses.</p>
<a href="{{ route('memo.index') }}" class="btn btn-primary">
Kembali ke Daftar
</a>
</div>
@endif
</form>
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
const selectAllCheckbox = document.getElementById('select-all');
const permohonanCheckboxes = document.querySelectorAll('.permohonan-checkbox');
const selectedCountSpan = document.getElementById('selected-count');
const submitBtn = document.getElementById('submit-btn');
const memoForm = document.getElementById('memo-form');
const totalBiayaPJInput = document.getElementById('total-biaya-pj');
/**
* Fungsi untuk mengupdate total biaya PJ
*/
function updateTotalBiayaPJ() {
const checkedBoxes = document.querySelectorAll('.permohonan-checkbox:checked');
const permohonanIds = Array.from(checkedBoxes).map(cb => cb.value);
if (permohonanIds.length === 0) {
if (totalBiayaPJInput) {
totalBiayaPJInput.value = 'Rp 0';
}
return;
}
// Kirim AJAX request untuk mendapatkan total biaya PJ
fetch('{{ route('memo.total-biaya-pj') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute(
'content')
},
body: JSON.stringify({
permohonan_ids: permohonanIds
})
})
.then(response => response.json())
.then(data => {
if (data.success && totalBiayaPJInput) {
totalBiayaPJInput.value = data.total_biaya_pj_formatted;
}
})
.catch(error => {
console.error('Error:', error);
if (totalBiayaPJInput) {
totalBiayaPJInput.value = 'Error menghitung total';
}
});
}
/**
* Fungsi untuk mengupdate jumlah item yang dipilih
*/
function updateSelectedCount() {
const checkedBoxes = document.querySelectorAll('.permohonan-checkbox:checked');
const count = checkedBoxes.length;
if (selectedCountSpan) {
selectedCountSpan.textContent = count;
}
// Disable submit button jika tidak ada yang dipilih
if (submitBtn) {
submitBtn.disabled = count === 0;
if (count === 0) {
submitBtn.innerHTML = '<i class="ki-filled ki-check"></i> Pilih minimal 1 permohonan';
submitBtn.classList.add('btn-disabled');
} else {
submitBtn.innerHTML =
`<i class="ki-filled ki-check"></i> Buat Memo Penyelesaian (${count} item)`;
submitBtn.classList.remove('btn-disabled');
}
}
// Update select all checkbox state
if (selectAllCheckbox) {
selectAllCheckbox.checked = count === permohonanCheckboxes.length;
selectAllCheckbox.indeterminate = count > 0 && count < permohonanCheckboxes.length;
}
// Update total biaya PJ
updateTotalBiayaPJ();
}
/**
* Event listener untuk select all checkbox
*/
if (selectAllCheckbox) {
selectAllCheckbox.addEventListener('change', function() {
const isChecked = this.checked;
permohonanCheckboxes.forEach(checkbox => {
checkbox.checked = isChecked;
});
updateSelectedCount();
});
}
/**
* Event listener untuk individual checkboxes
*/
permohonanCheckboxes.forEach(checkbox => {
checkbox.addEventListener('change', function() {
updateSelectedCount();
});
});
/**
* Event listener untuk form submission
*/
if (memoForm) {
memoForm.addEventListener('submit', function(e) {
const checkedBoxes = document.querySelectorAll('.permohonan-checkbox:checked');
if (checkedBoxes.length === 0) {
e.preventDefault();
alert('Pilih minimal 1 permohonan untuk diproses!');
return false;
}
// Konfirmasi sebelum submit
const confirmed = confirm(
`Apakah Anda yakin ingin membuat memo penyelesaian untuk ${checkedBoxes.length} permohonan?`
);
if (!confirmed) {
e.preventDefault();
return false;
}
// Disable submit button untuk mencegah double submit
if (submitBtn) {
submitBtn.disabled = true;
submitBtn.innerHTML = '<i class="ki-filled ki-loading"></i> Memproses...';
}
});
}
// Initialize count dan total biaya PJ
updateSelectedCount();
});
</script>
@endpush