Merge branch 'staging' into feature/senior-officer

This commit is contained in:
majid
2025-03-07 10:48:46 +07:00
18 changed files with 384 additions and 70 deletions

View File

@@ -120,8 +120,8 @@
<th class="min-w-[100px]">Tgl Assign</th>
<th class="min-w-[100px]">Tgl Kunjungan</th>
<th class="min-w-[100px]">Progress</th>
<th class="min-w-[100px]">Due Date SLA</th>
<th class="min-w-[100px]">Paparan</th>
<th class="min-w-[100px]">SLA Laporan</th>
<th class="min-w-[100px]">SLA Paparan</th>
<th class="min-w-[100px]">Approve</th>
<th class="min-w-[50px] text-center">Keterangan</th>
<th class="min-w-[50px] text-center">Action</th>
@@ -251,6 +251,15 @@
return `${window.formatTanggalIndonesia(data.due_date_sla)}`;
}
},
due_date: {
title: 'Due Date SLA',
render: (item, data) => {
if (!data.due_date_sla) {
return `-`;
}
return `${window.formatTanggalIndonesia(data.due_date_sla)}`;
},
},
paparan: {
title: 'Paparan',
@@ -258,7 +267,7 @@
if (!data.due_date_sla) {
return `-`;
}
return `${window.formatTanggalIndonesia(data.due_date_sla)}`;
return `${window.formatTanggalIndonesia(data.paparan)}`;
}
},
approve: {

View File

@@ -256,7 +256,7 @@
placeholder="Nama NPW">
<input type="text"
id="luas_npw_${npwCounter}"
id="ls_npw_${npwCounter}"
class="input w-full "
name="luas_npw_${npwCounter}"
placeholder="Luas NPW"
@@ -333,7 +333,7 @@
placeholder="Nama NPW"
value="${npw.name || ''}">
<input type="text"
id="luas_npw_${npwCounter}"
id="ls_npw_${npwCounter}"
class="input w-full currency-format"
name="luas_npw_${npwCounter}"
placeholder="Luas NPW"
@@ -459,6 +459,7 @@
const kategoriItems = document.querySelectorAll('[id^="luas_"]');
kategoriItems.forEach(item => {
const kategori = item.id.replace('luas_', '');
const luasInput = document.getElementById(`luas_${kategori}`);
const nilaiInput = document.querySelector(`input[name="nilai_${kategori}_1"]`);
@@ -477,7 +478,7 @@
// Tambahkan perhitungan untuk NPW tambahan
const npwRows = document.querySelectorAll('.npw-row');
npwRows.forEach(row => {
const luasInput = row.querySelector('input[id^="luas_npw_"]');
const luasInput = row.querySelector('input[id^="ls_npw_"]');
const nilaiInput = row.querySelector('input[id^="nilai_npw_"][id$="_1"]');
const outputElement = row.querySelector('input[id^="nilai_npw_"][id$="_2"]');
@@ -494,6 +495,7 @@
// Update total nilai pasar wajar
const totalElement = document.getElementById('total_nilai_pasar_wajar');
if (totalElement) {
totalElement.value = formatCurrency(totalNilaiPasarWajar.toString());
}

View File

@@ -93,6 +93,24 @@
@endsection
@push('scripts')
<script type="text/javascript">
function formatDate(date) {
const day = date.getDate().toString().padStart(2, '0');
const month = (date.getMonth() + 1).toString().padStart(2, '0');
// Months are 0-indexed
const year = date.getFullYear();
return `${day} ${getIndonesianMonth(month)} ${year}`;
}
function getIndonesianMonth(month) {
const months = ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni',
'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'
];
return months[month -
1];
}
</script>
<script type="module">
const element = document.querySelector('#laporan-table');
const searchInput = document.getElementById('search');
@@ -168,14 +186,40 @@
tanggal_survei: {
title: 'Tanggal Survei',
render: (item, data) => {
return '-';
}
if(data.penilaian.waktu_penilaian){
return `${formatDate(new Date(data.penilaian.waktu_penilaian))}`;
}
return `${formatDate(new Date(data.penilaian.created_at))}`;
},
},
due_date_sla: {
title: 'Due Date SLA',
render: (item, data) => {
return '-';
}
const tujuan_penilaian = data.tujuan_penilaian.name;
const tipe_laporan = data.penilai?.type;
const nilai_plafond = data.penilaian.nilaiPlafond?.name;
let waktu_penilaian = new Date(data.penilaian.created_at);
if(data.penilaian.waktu_penilaian){
waktu_penilaian = new Date(data.penilaian.waktu_penilaian);
}
if(tujuan_penilaian.name==="RAP"){
waktu_penilaian.setDate(waktu_penilaian.getDate() + 3);
} else {
if(tipe_laporan==="sederhana"){
waktu_penilaian.setDate(waktu_penilaian.getDate() + 2);
} else if(tipe_laporan==="standar"){
if(nilai_plafond==="2 M - 5 M"){
waktu_penilaian.setDate(waktu_penilaian.getDate() + 3);
} else if(nilai_plafond==="< 2M"){
waktu_penilaian.setDate(waktu_penilaian.getDate() + 3);
} else {
waktu_penilaian.setDate(waktu_penilaian.getDate() + 5);
}
}
}
return formatDate(waktu_penilaian);
},
},
status: {
title: 'Status',

View File

@@ -57,7 +57,7 @@
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">Penjual (HP)</label>
<div class="flex flex-wrap items-baseline w-full">
<span>{{ $item['telepon'] }}</span>
<span>{{ $item['telepon'] ?? '' }}</span>
</div>
</div>
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">

View File

@@ -295,7 +295,7 @@
<h1 class="text-md font-medium text-gray-900">Upload Foto</h1>
</div>
<div class="dropzone" id="upload-dropzone">
<div class="dropzone" id="dropzone-upload">
<div class="dz-message needsclick" data-foto-type="upload_foto">
<i class="ki-duotone ki-file-up text-primary text-3xl"><span class="path1"></span><span
class="path2"></span></i>
@@ -306,6 +306,9 @@
</div>
</div>
</div>
<div class="card-footer">
<div id="existing-photos" class="flex gap-5"></div>
</div>
</div>
{{-- @include('lpj::penilai.components.foto-lampiran') --}}
@@ -337,7 +340,75 @@
</div>
@endsection
@include('lpj::surveyor.js.utils')
<script>
@push('scripts')
<script>
Dropzone.autoDiscover = false;
let myDropzone;
document.addEventListener('DOMContentLoaded', function() {
myDropzone = new Dropzone("#dropzone-upload", {
url: "{{ route('penilai.uploadTempPhoto') }}", // Temporary upload route
paramName: "file",
maxFilesize: 5, // MB
acceptedFiles: "image/*",
addRemoveLinks: true,
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
init: function() {
this.on("success", function(file, response) {
file.serverId = response.id; // Store the server's file ID
});
// Load existing photos
loadExistingPhotos();
}
});
});
function loadExistingPhotos() {
const existingPhotosContainer = document.getElementById('existing-photos');
if (!existingPhotosContainer) return;
@if(isset($memo) && isset($memo->foto))
let existingPhotos;
try {
existingPhotos = @json($memo->foto);
} catch (e) {
console.error('Error parsing existing photos:', e);
return;
}
if (Array.isArray(existingPhotos)) {
existingPhotos.forEach(function(photoPath) {
if (typeof photoPath === 'string') {
const photoDiv = document.createElement('div');
photoDiv.className = 'col-md-3 mb-3';
const img = document.createElement('img');
img.src = photoPath;
img.className = 'img-fluid';
img.style.maxHeight = '150px';
photoDiv.appendChild(img);
existingPhotosContainer.appendChild(photoDiv);
if (myDropzone) {
let mockFile = { name: photoPath.split('/').pop(), size: 12345 };
myDropzone.emit("addedfile", mockFile);
myDropzone.emit("thumbnail", mockFile, photoPath);
myDropzone.emit("complete", mockFile);
mockFile.previewElement.classList.add("dz-success");
mockFile.previewElement.classList.add("dz-complete");
}
}
});
} else {
console.error('Existing photos is not an array:', existingPhotos);
}
@endif
}
function saveMemo() {
const form = document.getElementById('form-memo');
const formData = new FormData(form);
@@ -371,17 +442,26 @@
const documentId = urlParams.get('documentId');
const inspeksiId = urlParams.get('inspeksiId');
const requestUrl = `{{ route('penilai.storeMemo') }}`;
// Create a new FormData object to send both JSON and files
const sendFormData = new FormData();
sendFormData.append('permohonan_id', permohonanId);
sendFormData.append('document_id', documentId);
sendFormData.append('inspeksi_id', inspeksiId);
sendFormData.append('memo', JSON.stringify(jsonData));
// Append all files from Dropzone
myDropzone.getAcceptedFiles().forEach((file, index) => {
sendFormData.append(`foto_${index}`, file);
});
const requestUrl = `{{ route('penilai.storeMemoWithPhotos') }}`;
$.ajax({
url: requestUrl,
type: 'POST',
data: JSON.stringify({
permohonan_id: permohonanId,
document_id: documentId,
inspeksi_id: inspeksiId,
memo: jsonData,
}),
contentType: 'application/json',
data: sendFormData,
processData: false,
contentType: false,
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
@@ -409,17 +489,15 @@
console.log(response);
},
error: function(xhr, status, error) {
let errors = xhr.responseJSON?.errors;
$('.alert').text('');
if (errors) {
$.each(errors, function(key, value) {
$(`#error-${key}`).text(value[0]);
toastrErrorBuild(value[0]);
});
}
hideLoadingSwal();
console.log(errors);
Swal.fire({
title: 'Error!',
text: 'Terjadi kesalahan saat mengirim data',
icon: 'error',
confirmButtonText: 'OK'
});
}
});
}
</script>
@endpush

View File

@@ -60,6 +60,10 @@
<span class="sort"> <span class="sort-label"> Fasilitas Kredit </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="jenis_laporan">
<span class="sort"> <span class="sort-label"> Jenis Laporan </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="tanggal_survei">
<span class="sort"> <span class="sort-label"> Tanggal Survei </span>
<span class="sort-icon"> </span> </span>
@@ -170,16 +174,53 @@
return data.jenisfasilitas_kredit && data.jenisfasilitas_kredit.name ? `${data.jenisfasilitas_kredit.name}` : '-';
},
},
jenis_laporan: {
title: 'Jenis Laporan',
render: (item, data) => {
return data.penilai?.type;
},
},
tanggal_survei: {
title: 'Tanggal Survei',
render: (item, data) => {
return `${formatDate(new Date(data.created_at))}`;
if(data.penilaian.waktu_penilaian){
return `${formatDate(new Date(data.penilaian.waktu_penilaian))}`;
}
return `${formatDate(new Date(data.penilaian.created_at))}`;
},
},
due_date_sla: {
title: 'Due Date SLA',
render: (item, data) => {
return `${formatDate(new Date(data.created_at))}`;
const tujuan_penilaian = data.tujuan_penilaian.name;
const tipe_laporan = data.penilai?.type;
const nilai_plafond = data.penilaian.nilaiPlafond?.name;
let waktu_penilaian = new Date(data.penilaian.created_at);
if(data.penilaian.waktu_penilaian){
waktu_penilaian = new Date(data.penilaian.waktu_penilaian);
}
if(tujuan_penilaian.name==="RAP"){
waktu_penilaian.setDate(waktu_penilaian.getDate() + 3);
} else {
if(tipe_laporan==="sederhana"){
waktu_penilaian.setDate(waktu_penilaian.getDate() + 2);
} else if(tipe_laporan==="standar"){
if(nilai_plafond==="2 M - 5 M"){
waktu_penilaian.setDate(waktu_penilaian.getDate() + 3);
} else if(nilai_plafond==="< 2M"){
waktu_penilaian.setDate(waktu_penilaian.getDate() + 3);
} else {
waktu_penilaian.setDate(waktu_penilaian.getDate() + 5);
}
}
}
return formatDate(waktu_penilaian);
},
},
status: {

View File

@@ -110,7 +110,7 @@
@endif
@if ($permohonan->authorization->approve_so && $dataHeader == 'paparan' )
@if (isset($permohonan->authorization->approve_so) && $dataHeader == 'paparan' )
<div class="card border border-agi-100 pb-2.5">
<div class="card-header bg-agi-50" id="basic_settings">
<h3 class="card-title">
@@ -217,7 +217,7 @@
@if (Auth::user()->hasAnyRole(['administrator', 'senior-officer']) && $authorization->approve_so == null)
<button onclick="otorisatorData({{ $permohonan->id }},'SO')" type="button"
<button onclick="otorisatorData({{ $authorization->id }},'SO')" type="button"
class="btn btn-primary">
<i class="ki-filled ki-double-check"></i>
Otorisator {{ $header ?? '' }}
@@ -225,7 +225,7 @@
@endif
@if (Auth::user()->hasAnyRole(['administrator', 'EO Appraisal']) && $authorization->approve_so && $authorization->approve_eo == null)
<button onclick="otorisatorData({{ $permohonan->id }},'EO')" type="button"
<button onclick="otorisatorData({{ $authorization->id }},'EO')" type="button"
class="btn btn-primary">
<i class="ki-filled ki-double-check"></i>
Otorisator {{ $header ?? '' }}
@@ -233,7 +233,7 @@
@endif
@if (Auth::user()->hasAnyRole(['administrator', 'DD Appraisal']) && $authorization->approve_eo && $authorization->approve_dd == null)
<button onclick="otorisatorData({{ $permohonan->id }},'DD')" type="button"
<button onclick="otorisatorData({{ $authorization->id }},'DD')" type="button"
class="btn btn-primary">
<i class="ki-filled ki-double-check"></i>
Otorisator {{ $header ?? '' }}
@@ -247,7 +247,7 @@
@push('scripts')
<script>
const handleRejection = (dataId) => {
const handleRejection = (dataId,dataHeader='') => {
Swal.fire({
title: 'Masukkan alasan penolakan:',
input: 'textarea',
@@ -268,7 +268,8 @@
if (rejectResult.isConfirmed) {
handleAjaxRequest(
`/otorisator/revisi-laporan/${dataId}`, {
keterangan: rejectResult.value
keterangan: rejectResult.value,
dataHeader: dataHeader
},
'Data berhasil ditolak.',
'Terjadi kesalahan saat melakukan penolakan.'
@@ -383,7 +384,7 @@
'Terjadi kesalahan saat melakukan otorisasi.'
);
} else if (result.isDenied) {
handleRejection(dataId);
handleRejection(dataId,dataHeader);
}
});
}

View File

@@ -84,6 +84,18 @@
<em id="{{$route[0]}}_region_msg" class="alert text-danger text-sm"></em>
</div>
</div>
<div id="{{ $route[0] }}_div_jenis_laporan" class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Jenis Laporan
</label>
<div class="flex flex-wrap items-baseline w-full">
<select class="inputku select" id="jenis_laporan" name="jenis_laporan">
<option value="sederhana">Sederhana</option>
<option value="standar">Standar</option>
</select>
<em id="jenis_laporan_msg" class="alert text-danger text-sm"></em>
</div>
</div>
<div id="{{ $route[0] }}_div_catatan2" class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Catatan

View File

@@ -120,6 +120,7 @@
let region = $("#{{$route[0]}}_region").val();
let catatan = $("#{{$route[0]}}_catatan").val();
let catatan2 = $("#{{$route[0]}}_catatan2").val();
let jenis_laporan = $("#jenis_laporan").val();
if(jenis_penilaian==0)
jenis_penilaian='';
@@ -135,6 +136,7 @@
input_data.region= region;
input_data.catatan = catatan;
input_data.catatan2 = catatan2;
input_data.jenis_laporan = jenis_laporan;
let useURL= '{{ route($route[0].'.update', $id) }}';
$.ajax({

View File

@@ -81,7 +81,19 @@
</select>
<em id="{{ $route[0] }}_region_msg" class="alert text-danger text-sm"></em>
</div>
</div><br />
</div>
<div id="{{ $route[0] }}_div_jenis_laporan" class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Jenis Laporan
</label>
<div class="flex flex-wrap items-baseline w-full">
<select class="inputku select" id="jenis_laporan" name="jenis_laporan">
<option value="sederhana">Sederhana</option>
<option value="standar">Standar</option>
</select>
<em id="jenis_laporan_msg" class="alert text-danger text-sm"></em>
</div>
</div>
<div id="{{ $route[0] }}_div_catatan2"
class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">

View File

@@ -60,8 +60,8 @@
</center>
<br>
<h3>Kepada</h3>
<p style="color: red; margin-left:25px">KJPP {{ $penawaran->kjpp_name }}</p>
<p style="color: red; margin-left:25px">{{ $penawaran->kjpp_address }}</p>
<p style="color: red;">KJPP {{ $penawaran->kjpp_name }}</p>
<p style="color: red;">{{ $penawaran->kjpp_address }}</p>
<br/>
<br/>
<p style="text-align: justify;">

View File

@@ -76,8 +76,8 @@
</center>
<br>
<h3>Kepada</h3>
<p style="color: red; margin-left:25px">KJPP {{ $penawaran->kjpp_name }}</p>
<p style="color: red; margin-left:25px">{{ $penawaran->kjpp_address }}</p>
<p style="color: red;">KJPP {{ $penawaran->kjpp_name }}</p>
<p style="color: red;">{{ $penawaran->kjpp_address }}</p>
<br/>
<br/>
<h3>Perihal: <b>Penunjukan sebagai Penyedia Jasa Penilaian Agunan</b></h3>