🔧 refactor(inspeksi): gunakan updateOrCreate & perbaikan kode

- Ganti `Inspeksi::create()` → `updateOrCreate()` di PenilaiController (2x) & SurveyorController (1x) dengan kondisi upsert (permohonan_id + dokument_id)
- Tambah logging di SaveFormInspesksiService.php (`Log::info`) untuk debugging & validasi action kosong
- Perbaiki error handling dengan pesan lebih informatif `'Gagal menyimpan data : '.$e->getMessage()`
- Refaktor parsing action memakai array_map & array_filter agar lebih efisien
- Rapikan kode: hapus baris kosong tidak perlu & improve readability
- Perbaiki urutan class CSS di beberapa Blade view (rap-penilai, penilai/index, surveyor/inspeksi)
- Perbaiki XSS di rap-penilai.blade.php dengan `{!! json_encode($dokumen->address ?? '') !!}`
- Tingkatkan integritas database: cegah duplikasi data inspeksi via updateOrCreate()
- Tambah keamanan & maintainability: logging, validasi input, perbaikan format, serta pembersihan kode lama
This commit is contained in:
Daeng Deni Mardaeni
2025-09-19 09:06:12 +07:00
parent 17f7482080
commit ee7c8ce97f
6 changed files with 181 additions and 182 deletions

View File

@@ -1006,11 +1006,10 @@ class PenilaiController extends Controller
], ],
]; ];
Inspeksi::updateOrCreate([
Inspeksi::create([
'permohonan_id' => $validatedData['permohonan_id'], 'permohonan_id' => $validatedData['permohonan_id'],
'dokument_id' => $validatedData['dokument_id'], 'dokument_id' => $validatedData['dokument_id']
],[
'data_form' => json_encode($newData), 'data_form' => json_encode($newData),
'name' => $validatedData['type'] 'name' => $validatedData['type']
]); ]);
@@ -1252,9 +1251,10 @@ class PenilaiController extends Controller
Inspeksi::create([ Inspeksi::updateOrCreate([
'permohonan_id' => $validated['permohonan_id'], 'permohonan_id' => $validated['permohonan_id'],
'dokument_id' => $validated['dokument_id'], 'dokument_id' => $validated['dokument_id']
],[
'data_form' => json_encode($newData), 'data_form' => json_encode($newData),
'name' => $validated['type'] 'name' => $validated['type']
]); ]);

View File

@@ -2880,9 +2880,10 @@ class SurveyorController extends Controller
$inspeksi->data_form = json_encode($existingData); $inspeksi->data_form = json_encode($existingData);
$inspeksi->save(); $inspeksi->save();
} else { } else {
Inspeksi::create([ Inspeksi::updateOrCreate([
'permohonan_id' => $request->input('permohonan_id'), 'permohonan_id' => $request->input('permohonan_id'),
'dokument_id' => $request->input('document_id'), 'dokument_id' => $request->input('document_id')
],[
'data_form' => json_encode($existingData), 'data_form' => json_encode($existingData),
]); ]);
} }

View File

@@ -4,6 +4,7 @@ namespace Modules\Lpj\Services;
use Modules\Lpj\Models\Inspeksi; use Modules\Lpj\Models\Inspeksi;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use \Illuminate\Support\Facades\Log;
class SaveFormInspesksiService class SaveFormInspesksiService
{ {
@@ -20,6 +21,7 @@ class SaveFormInspesksiService
$inspeksi->name = $request->input('type'); $inspeksi->name = $request->input('type');
$processedData = $this->getActionSpecificRules($validatedData, $type, $request, $inspeksi); $processedData = $this->getActionSpecificRules($validatedData, $type, $request, $inspeksi);
Log::info($processedData);
// Merge data lama dengan data baru // Merge data lama dengan data baru
$existingData = json_decode($inspeksi->data_form, true) ?: []; $existingData = json_decode($inspeksi->data_form, true) ?: [];
@@ -33,6 +35,7 @@ class SaveFormInspesksiService
'upload_gs' 'upload_gs'
]; ];
foreach ($fotoTypes as $fotoType) { foreach ($fotoTypes as $fotoType) {
if (isset($existingData[$fotoType])) { if (isset($existingData[$fotoType])) {
$processedData[$fotoType] = $existingData[$fotoType]; $processedData[$fotoType] = $existingData[$fotoType];
@@ -57,7 +60,7 @@ class SaveFormInspesksiService
} catch (\Exception $e) { } catch (\Exception $e) {
return [ return [
'success' => false, 'success' => false,
'message' => 'Gagal menyimpan data', 'message' => 'Gagal menyimpan data : '.$e->getMessage(),
'error' => $e->getMessage() 'error' => $e->getMessage()
]; ];
} }
@@ -85,36 +88,31 @@ class SaveFormInspesksiService
$hasAssetDescriptionRules = false; $hasAssetDescriptionRules = false;
$hasFactaData = false; $hasFactaData = false;
$actions = array_map('trim', explode(',', $action));
$pisah = array_filter($actions, function($act) use ($allowedActions) {
$pisah = array_filter( return isset($allowedActions[$act]);
explode(',', $action), });
function ($act) use ($allowedActions) {
return isset($allowedActions[trim($act)]);
}
);
// dd($pisah);
foreach ($pisah as $act) { foreach ($pisah as $act) {
$act = trim($act); // Bersihkan spasi $act = trim($act); // Bersihkan spasi
if (isset($allowedActions[$act])) { if($act){
$method = $allowedActions[$act]; if (isset($allowedActions[$act])) {
$method = $allowedActions[$act];
$actionRules = $this->$method($data, $request, $inspeksi); $actionRules = $this->$method($data, $request, $inspeksi);
$rules = array_merge($rules, $actionRules); $rules = array_merge($rules, $actionRules);
// Cek apakah act memerlukan asset description rules
if($act){
if (in_array($act, ['apartemen-kantor', 'tanah', 'bangunan', 'rap'])) {
$hasAssetDescriptionRules = true;
}
// Cek apakah act memerlukan asset description rules if (in_array($act, ['rap'])) {
if (in_array($act, ['apartemen-kantor', 'tanah', 'bangunan', 'rap'])) { $hasFactaData = true;
$hasAssetDescriptionRules = true; }
} }
// Cek apakah act memerlukan fakta data
if (in_array($act, ['rap'])) {
$hasFactaData = true;
} }
} }
} }
if ($hasAssetDescriptionRules) { if ($hasAssetDescriptionRules) {
@@ -450,8 +448,6 @@ class SaveFormInspesksiService
$data['perizinan'] = $perizinanData; $data['perizinan'] = $perizinanData;
$partisiResult = []; $partisiResult = [];
if (isset($data['partisi'])) { if (isset($data['partisi'])) {
foreach ($data['partisi'] as $name => $values) { foreach ($data['partisi'] as $name => $values) {

View File

@@ -5,7 +5,7 @@
@endsection @endsection
@section('content') @section('content')
<div class="w-full grid gap-5 lg:gap-7.5 mx-auto"> <div class="grid gap-5 mx-auto w-full lg:gap-7.5">
<form id="formInspeksi" method="POST" enctype="multipart/form-data" class="grid gap-5"> <form id="formInspeksi" method="POST" enctype="multipart/form-data" class="grid gap-5">
@csrf @csrf
<input type="hidden" name="nomor_registrasi" value="{{ $permohonan->nomor_registrasi }}"> <input type="hidden" name="nomor_registrasi" value="{{ $permohonan->nomor_registrasi }}">
@@ -16,48 +16,48 @@
@include('lpj::assetsku.includenya') @include('lpj::assetsku.includenya')
<div class="card"> <div class="card">
<div class="card-header bg-agi-50"> <div class="card-header bg-agi-50">
<h3 class="card-title uppercase"> <h3 class="uppercase card-title">
RAP RAP
</h3> </h3>
</div> </div>
<div class="card-body grid gap-5"> <div class="grid gap-5 card-body">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5"> <div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
<label class="form-label max-w-56">Kepada</label> <label class="form-label max-w-56">Kepada</label>
<div class="flex flex-wrap items-baseline w-full"> <div class="flex flex-wrap items-baseline w-full">
<input type="text" name="kepada" class="input w-full" placeholder="Masukkan..." <input type="text" name="kepada" class="w-full input" placeholder="Masukkan..."
value=" {{ $rap['kepada'] ?? '' }}"> value=" {{ $rap['kepada'] ?? '' }}">
</div> </div>
</div> </div>
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5"> <div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
<label class="form-label max-w-56">Dari</label> <label class="form-label max-w-56">Dari</label>
<div class="flex flex-wrap items-baseline w-full"> <div class="flex flex-wrap items-baseline w-full">
<input type="text" name="dari" class="input w-full" placeholder="Masukkan..." <input type="text" name="dari" class="w-full input" placeholder="Masukkan..."
value="{{ $rap['dari'] ?? '' }}"> value="{{ $rap['dari'] ?? '' }}">
</div> </div>
</div> </div>
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5"> <div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
<label class="form-label max-w-56">Nomor RAP</label> <label class="form-label max-w-56">Nomor RAP</label>
<div class="flex flex-wrap items-base line w-full"> <div class="flex flex-wrap w-full items-base line">
<input type="text" name="nomor_rap" class="input w-full" placeholder="Masukkan..." <input type="text" name="nomor_rap" class="w-full input" placeholder="Masukkan..."
value="{{ $nomorLaporan ?? '' }}" @readonly(true)> value="{{ $nomorLaporan ?? '' }}" @readonly(true)>
</div> </div>
</div> </div>
{{-- 250109828129/ --}} {{-- 250109828129/ --}}
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5"> <div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
<label class="form-label max-w-56">Tanggal</label> <label class="form-label max-w-56">Tanggal</label>
<div class="flex flex-wrap items-baseline w-full"> <div class="flex flex-wrap items-baseline w-full">
<input type="date" name="tanggal" class="input w-full" placeholder="Masukkan..." <input type="date" name="tanggal" class="w-full input" placeholder="Masukkan..."
value="{{ $rap['tanggal'] ?? '' }}"> value="{{ $rap['tanggal'] ?? '' }}">
</div> </div>
</div> </div>
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5"> <div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
<label class="form-label max-w-56">Perihal</label> <label class="form-label max-w-56">Perihal</label>
<div class="flex flex-wrap items-base line w-full"> <div class="flex flex-wrap w-full items-base line">
<input type="text" name="perihal" class="input w-full" placeholder="Masukkan..." <input type="text" name="perihal" class="w-full input" placeholder="Masukkan..."
value="{{ $rap['perihal'] ?? '' }}"> value="{{ $rap['perihal'] ?? '' }}">
</div> </div>
</div> </div>
@@ -65,7 +65,7 @@
</div> </div>
@include('lpj::surveyor.components.header') @include('lpj::surveyor.components.header')
@include('lpj::surveyor.components.rap') @include('lpj::surveyor.components.rap')
<div class="flex justify-end gap-2" style="margin-right: 20px; margin-top: 20px"> <div class="flex gap-2 justify-end" style="margin-right: 20px; margin-top: 20px">
@if (Auth::user()->hasAnyRole(['senior-officer', 'surveyor', 'administrator'])) @if (Auth::user()->hasAnyRole(['senior-officer', 'surveyor', 'administrator']))
<button type="button" class="btn btn-primary" id="saveButton" onclick="submitData()" <button type="button" class="btn btn-primary" id="saveButton" onclick="submitData()"
{{ $permohonan->status == 'proses-paparan' || ($permohonan->status == 'proses-laporan' && Auth::user()->hasAnyRole(['surveyor'])) ? 'disabled' : '' }}> {{ $permohonan->status == 'proses-paparan' || ($permohonan->status == 'proses-laporan' && Auth::user()->hasAnyRole(['surveyor'])) ? 'disabled' : '' }}>
@@ -107,7 +107,8 @@
const addressInput = document.getElementById('address'); const addressInput = document.getElementById('address');
if (status === 'sesuai') { if (status === 'sesuai') {
addressInput.value = "{{ $dokumen->address ?? '' }}"; addressInput.value = {!! json_encode($dokumen->address ?? '') !!};
inputs.forEach(element => { inputs.forEach(element => {
if (element.tagName === 'INPUT') { if (element.tagName === 'INPUT') {
element.setAttribute('readonly', true); element.setAttribute('readonly', true);

View File

@@ -5,12 +5,12 @@
@endsection @endsection
@section('content') @section('content')
<div class="w-full grid gap-5 lg:gap-7.5 mx-auto"> <div class="grid gap-5 mx-auto w-full lg:gap-7.5">
<div class="card border border-agi-100 card-grid min-w-full" data-datatable="false" data-datatable-page-size="10" <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="penilai-table" data-api-url="{{ route('penilai.dataForTables') }}"> data-datatable-state-save="false" id="penilai-table" data-api-url="{{ route('penilai.dataForTables') }}">
<div class="card-header bg-agi-50 py-5 flex-wrap"> <div class="flex-wrap py-5 card-header bg-agi-50">
<h3 class="card-title"> <h3 class="card-title">
Penilai Penilai
</h3> </h3>
@@ -29,7 +29,7 @@
<div class="card-body"> <div class="card-body">
<div class="scrollable-x-auto"> <div class="scrollable-x-auto">
<table class="table table-auto table-border align-middle text-gray-700 font-medium text-sm" <table class="table text-sm font-medium text-gray-700 align-middle table-auto table-border"
data-datatable-table="true"> data-datatable-table="true">
<thead> <thead>
<tr> <tr>
@@ -82,13 +82,13 @@
</table> </table>
</div> </div>
<div <div
class="card-footer justify-center md:justify-between flex-col md:flex-row gap-3 text-gray-600 text-2sm font-medium"> 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 items-center gap-2"> <div class="flex gap-2 items-center">
Show Show
<select class="select select-sm w-16" data-datatable-size="true" name="perpage"> </select> per <select class="w-16 select select-sm" data-datatable-size="true" name="perpage"> </select> per
page page
</div> </div>
<div class="flex items-center gap-4"> <div class="flex gap-4 items-center">
<span data-datatable-info="true"> </span> <span data-datatable-info="true"> </span>
<div class="pagination" data-datatable-pagination="true"> <div class="pagination" data-datatable-pagination="true">
</div> </div>
@@ -228,7 +228,7 @@
status: { status: {
title: 'Status', title: 'Status',
render: (item, data) => { render: (item, data) => {
return `<span class="badge badge-sm badge-default uppercase flex justify-center">${data.status.replace(/-/g, ' ')}</span>`; return `<span class="flex justify-center uppercase badge badge-sm badge-default">${data.status.replace(/-/g, ' ')}</span>`;
}, },
}, },
actions: { actions: {
@@ -237,14 +237,15 @@
if (data.status === 'survey-completed' || data.status === 'proses-laporan' || data if (data.status === 'survey-completed' || data.status === 'proses-laporan' || data
.status === 'paparan' || data.status === 'proses-paparan' || data.status === .status === 'paparan' || data.status === 'proses-paparan' || data.status ===
'paparan' || data.status == 'revisi-laporan' || data.status === 'done' || data 'paparan' || data.status == 'revisi-laporan' || data.status === 'done' || data
.status === 'revisi-paparan' || data.status === 'unfreeze-sla' || data.status === 'reject-freeze') { .status === 'revisi-paparan' || data.status === 'unfreeze-sla' || data.status ===
'reject-freeze') {
return ` return `
<div class="flex flex-nowrap justify-center gap-1.5"> <div class="flex flex-nowrap gap-1.5 justify-center">
<a class="btn btn-sm btn-icon btn-clear btn-info" href="penilai/${data.id}/show"> <a class="btn btn-sm btn-icon btn-clear btn-info" href="penilai/${data.id}/show">
<i class="ki-outline ki-eye"></i> <i class="ki-outline ki-eye"></i>
</a> </a>
<button type="button" <button type="button"
class="btn btn-sm btn-icon btn-clear btn-warning" class="btn btn-sm btn-icon btn-clear btn-warning"
onclick="surveyorFreeze('${data.id}', '${data.nomor_registrasi}', '${data.debiture?.name}')" onclick="surveyorFreeze('${data.id}', '${data.nomor_registrasi}', '${data.debiture?.name}')"
title="Freeze SLA"> title="Freeze SLA">
<i class="ki-filled ki-arrow-circle-right"></i> <i class="ki-filled ki-arrow-circle-right"></i>
@@ -257,13 +258,13 @@
<i class="ki-filled ki-watch"></i> <i class="ki-filled ki-watch"></i>
</a> </a>
</div>`; </div>`;
}else if(data.status === 'freeze'){ } else if (data.status === 'freeze') {
return ` <div class="flex flex-nowrap justify-center gap-1.5"> return ` <div class="flex flex-nowrap gap-1.5 justify-center">
<a class="btn btn-sm btn-icon btn-clear btn-info" href="penilai/${data.id}/show"> <a class="btn btn-sm btn-icon btn-clear btn-info" href="penilai/${data.id}/show">
<i class="ki-outline ki-eye"></i> <i class="ki-outline ki-eye"></i>
</a> </a>
</div>` </div>`
} else{ } else {
return `<div class="flex flex-nowrap justify-center"> return `<div class="flex flex-nowrap justify-center">
<a class="btn btn-sm btn-icon btn-clear btn-success" onclick="showLoadingSwal('Masih Menunggu proses ...')"> <a class="btn btn-sm btn-icon btn-clear btn-success" onclick="showLoadingSwal('Masih Menunggu proses ...')">
<i class="ki-filled ki-watch"></i> <i class="ki-filled ki-watch"></i>

View File

@@ -6,7 +6,7 @@
@section('content') @section('content')
@include('lpj::assetsku.includenya') @include('lpj::assetsku.includenya')
<div class="w-full grid gap-5 lg:gap-7.5 mx-auto"> <div class="grid gap-5 mx-auto w-full lg:gap-7.5">
<form id="formInspeksi" method="POST" enctype="multipart/form-data" class="grid gap-5"> <form id="formInspeksi" method="POST" enctype="multipart/form-data" class="grid gap-5">
@csrf @csrf
<input id="permohonan_id" type="hidden" name="permohonan_id" value="{{ $permohonan->id }}"> <input id="permohonan_id" type="hidden" name="permohonan_id" value="{{ $permohonan->id }}">
@@ -46,14 +46,14 @@
@endif @endif
@endforeach @endforeach
@endif @endif
<div class="card border border-agi-100 w-full rounded-lg shadow-md overflow-hidden"> <div class="overflow-hidden w-full rounded-lg border shadow-md card border-agi-100">
<div class="card-header light:bg-agi-50"> <div class="card-header light:bg-agi-50">
<h3 class="card-title uppercase"> <h3 class="uppercase card-title">
Tanda Tangan Tanda Tangan
</h3> </h3>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="flex items-baseline justify-between flex-wrap lg:flex-nowrap"> <div class="flex flex-wrap justify-between items-baseline lg:flex-nowrap">
@foreach (['penilai', 'cabang', 'debitur', 'kjpp'] as $type) @foreach (['penilai', 'cabang', 'debitur', 'kjpp'] as $type)
@include('lpj::component.signature-pad', ['type' => $type]) @include('lpj::component.signature-pad', ['type' => $type])
@endforeach @endforeach
@@ -65,7 +65,7 @@
</div> </div>
</div> </div>
<div class="flex justify-end gap-2" style="margin-right: 20px; margin-top: 20px"> <div class="flex gap-2 justify-end" style="margin-right: 20px; margin-top: 20px">
<button type="button" class="btn btn-primary" id="saveButton" onclick="submitData()"> <button type="button" class="btn btn-primary" id="saveButton" onclick="submitData()">
<i class="ki-filled ki-save-2"></i> <i class="ki-filled ki-save-2"></i>
<span id="saveButtonText">Simpan</span> <span id="saveButtonText">Simpan</span>
@@ -95,122 +95,122 @@
console.log(datas); console.log(datas);
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
const signaturePads = {}; const signaturePads = {};
const types = ['penilai', 'cabang', 'debitur', 'kjpp']; const types = ['penilai', 'cabang', 'debitur', 'kjpp'];
// Initialize all signature pads // Initialize all signature pads
types.forEach(type => initSignaturePad(type)); types.forEach(type => initSignaturePad(type));
function initSignaturePad(type) { function initSignaturePad(type) {
const canvas = document.getElementById(`signature-pad-${type}`); const canvas = document.getElementById(`signature-pad-${type}`);
if (!canvas) return; if (!canvas) return;
// Improved canvas sizing with strict boundary control // Improved canvas sizing with strict boundary control
function resizeCanvas() { function resizeCanvas() {
const container = canvas.closest('.signature-pad-container'); const container = canvas.closest('.signature-pad-container');
const containerWidth = container.clientWidth; const containerWidth = container.clientWidth;
// Set canvas style dimensions // Set canvas style dimensions
canvas.style.width = '100%'; canvas.style.width = '100%';
canvas.style.height = `${containerWidth * 0.5}px`; // 2:1 aspect ratio canvas.style.height = `${containerWidth * 0.5}px`; // 2:1 aspect ratio
// Set actual canvas dimensions with high DPI support // Set actual canvas dimensions with high DPI support
const ratio = window.devicePixelRatio || 1; const ratio = window.devicePixelRatio || 1;
canvas.width = containerWidth * ratio; canvas.width = containerWidth * ratio;
canvas.height = (containerWidth * 0.5) * ratio; canvas.height = (containerWidth * 0.5) * ratio;
// Scale canvas context // Scale canvas context
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext('2d');
ctx.scale(ratio, ratio); ctx.scale(ratio, ratio);
// Clear and redraw existing signature if any // Clear and redraw existing signature if any
if (signaturePads[type] && !signaturePads[type].isEmpty()) { if (signaturePads[type] && !signaturePads[type].isEmpty()) {
const signaturePad = signaturePads[type]; const signaturePad = signaturePads[type];
const imageData = signaturePad.toData(); const imageData = signaturePad.toData();
signaturePad.clear(); signaturePad.clear();
signaturePad.fromData(imageData); signaturePad.fromData(imageData);
} }
}
// Create signature pad with boundary and scaling control
const signaturePad = new SignaturePad(canvas, {
backgroundColor: 'rgba(255, 255, 255, 0)',
penColor: 'rgb(0, 0, 0)',
minWidth: 0.5,
maxWidth: 2.5,
throttle: 16,
dotSize: 2,
// Custom function to control signature drawing
onBegin: (event) => {
const ctx = canvas.getContext('2d');
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
// Ensure drawing stays within canvas
if (
event.clientX < rect.left ||
event.clientX > rect.right ||
event.clientY < rect.top ||
event.clientY > rect.bottom
) {
return false;
} }
// Create signature pad with boundary and scaling control
const signaturePad = new SignaturePad(canvas, {
backgroundColor: 'rgba(255, 255, 255, 0)',
penColor: 'rgb(0, 0, 0)',
minWidth: 0.5,
maxWidth: 2.5,
throttle: 16,
dotSize: 2,
// Custom function to control signature drawing
onBegin: (event) => {
const ctx = canvas.getContext('2d');
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
// Ensure drawing stays within canvas
if (
event.clientX < rect.left ||
event.clientX > rect.right ||
event.clientY < rect.top ||
event.clientY > rect.bottom
) {
return false;
}
}
});
signaturePads[type] = signaturePad;
// Initial resize
resizeCanvas();
// Load existing signature
if (type === 'penilai' || type === 'cabang') {
loadPenilaiAndCabangSignature(type, signaturePad);
} else {
loadSignature(type, signaturePad);
}
// Add event listeners
addEventListeners(type, signaturePad);
// Add resize listener
window.addEventListener('resize', () => {
resizeCanvas();
});
} }
});
signaturePads[type] = signaturePad;
// Initial resize function drawSignature(signaturePad, imageUrl) {
resizeCanvas(); const image = new Image();
image.crossOrigin = 'Anonymous';
image.onload = function() {
const ctx = signaturePad.canvas.getContext('2d');
const canvasWidth = signaturePad.canvas.width;
const canvasHeight = signaturePad.canvas.height;
// Load existing signature // Clear previous content
if (type === 'penilai' || type === 'cabang') { ctx.clearRect(0, 0, canvasWidth, canvasHeight);
loadPenilaiAndCabangSignature(type, signaturePad);
} else {
loadSignature(type, signaturePad);
}
// Add event listeners // Calculate scaling to fit within canvas while maintaining aspect ratio
addEventListeners(type, signaturePad); const scale = Math.min(
canvasWidth / image.width,
canvasHeight / image.height
);
// Add resize listener const scaledWidth = image.width * scale;
window.addEventListener('resize', () => { const scaledHeight = image.height * scale;
resizeCanvas();
});
}
function drawSignature(signaturePad, imageUrl) { // Center the image
const image = new Image(); const x = (canvasWidth - scaledWidth) / 2;
image.crossOrigin = 'Anonymous'; const y = (canvasHeight - scaledHeight) / 2;
image.onload = function() {
const ctx = signaturePad.canvas.getContext('2d');
const canvasWidth = signaturePad.canvas.width;
const canvasHeight = signaturePad.canvas.height;
// Clear previous content // Draw the scaled and centered image
ctx.clearRect(0, 0, canvasWidth, canvasHeight); ctx.drawImage(image, x, y, scaledWidth, scaledHeight);
};
// Calculate scaling to fit within canvas while maintaining aspect ratio image.onerror = function() {
const scale = Math.min( console.error('Error loading signature image');
canvasWidth / image.width, };
canvasHeight / image.height image.src = imageUrl;
); }
const scaledWidth = image.width * scale;
const scaledHeight = image.height * scale;
// Center the image
const x = (canvasWidth - scaledWidth) / 2;
const y = (canvasHeight - scaledHeight) / 2;
// Draw the scaled and centered image
ctx.drawImage(image, x, y, scaledWidth, scaledHeight);
};
image.onerror = function() {
console.error('Error loading signature image');
};
image.src = imageUrl;
}
function addEventListeners(type, signaturePad) { function addEventListeners(type, signaturePad) {
document.getElementById(`save-${type}`)?.addEventListener('click', () => saveSignature(type, document.getElementById(`save-${type}`)?.addEventListener('click', () => saveSignature(type,
@@ -519,17 +519,17 @@
const formData = new FormData(form); const formData = new FormData(form);
const fotoFields = [ const fotoFields = [
'foto_gistaru', 'foto_gistaru',
'foto_bhumi', 'foto_bhumi',
'foto_argis_region', 'foto_argis_region',
'foto_tempat', 'foto_tempat',
'foto_sentuh_tanahku', 'foto_sentuh_tanahku',
'upload_gs' 'upload_gs'
]; ];
fotoFields.forEach((field) => { fotoFields.forEach((field) => {
formData.delete(field); formData.delete(field);
}); });
$.ajax({ $.ajax({
url: '{{ route('surveyor.store') }}', url: '{{ route('surveyor.store') }}',
type: 'POST', type: 'POST',