feat(signature): penambahan tanda tangan di surveyor
This commit is contained in:
@@ -165,17 +165,34 @@ class SurveyorController extends Controller
|
||||
$request
|
||||
);
|
||||
|
||||
// Find or create inspeksi record
|
||||
$inspeksi = Inspeksi::updateOrCreate(
|
||||
[
|
||||
$inspeksi = Inspeksi::where('permohonan_id', $request->input('permohonan_id'))
|
||||
->where('dokument_id', $request->input('dokument_id'))
|
||||
->first();
|
||||
|
||||
if ($inspeksi) {
|
||||
// Jika data sudah ada, merge dengan data yang baru
|
||||
$existingData = json_decode($inspeksi->data_form, true) ?: [];
|
||||
|
||||
$mergedData = $this->arrayMergeRecursive($existingData, $processedData);
|
||||
|
||||
// Update record
|
||||
$inspeksi->update([
|
||||
'data_form' => json_encode($mergedData),
|
||||
'name' => $request->input('type')
|
||||
]);
|
||||
|
||||
$responseData = $mergedData;
|
||||
} else {
|
||||
// Jika belum ada data, buat record baru
|
||||
$inspeksi = Inspeksi::create([
|
||||
'permohonan_id' => $request->input('permohonan_id'),
|
||||
'dokument_id' => $request->input('dokument_id')
|
||||
],
|
||||
[
|
||||
'dokument_id' => $request->input('dokument_id'),
|
||||
'data_form' => json_encode($processedData),
|
||||
'name' => $request->input('type')
|
||||
]
|
||||
);
|
||||
]);
|
||||
|
||||
$responseData = $processedData;
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
@@ -192,6 +209,7 @@ class SurveyorController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function getActionSpecificRules($data, $action, $request): array
|
||||
{
|
||||
$allowedActions = [
|
||||
@@ -3280,13 +3298,9 @@ class SurveyorController extends Controller
|
||||
}
|
||||
|
||||
|
||||
public function signature()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function signatureStore(Request $request)
|
||||
{
|
||||
|
||||
$validator = Validator::make($request->all(), [
|
||||
'signature' => 'required',
|
||||
'type' => 'required|in:penilai,cabang,debitur,kjjp',
|
||||
@@ -3300,34 +3314,168 @@ class SurveyorController extends Controller
|
||||
], 422);
|
||||
}
|
||||
|
||||
// Hapus prefix data:image/png;base64,
|
||||
$image = explode(',', $request->signature)[1];
|
||||
$imageName = 'signatures/' . $request->type . '_' . time() . '.png';
|
||||
try {
|
||||
|
||||
// Simpan file
|
||||
Storage::disk('public')->put($imageName, base64_decode($image));
|
||||
$existingData = $inspeksi->exists && $inspeksi->foto_form
|
||||
? json_decode($inspeksi->foto_form, true)
|
||||
: [];
|
||||
$inspeksi = Inspeksi::where('permohonan_id', $request->input('permohonan_id'))
|
||||
->where('dokument_id', $request->input('document_id'))
|
||||
->first();
|
||||
|
||||
$formatFotojson = $existingData;
|
||||
$inspeksi = Inspeksi::save(
|
||||
[
|
||||
'permohonan_id' => $request->input('permohonan_id'),
|
||||
'dokument_id' => $request->input('dokument_id')
|
||||
],
|
||||
[
|
||||
'data_form' => json_encode($processedData),
|
||||
if (!$inspeksi) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Data inspeksi tidak ditemukan'
|
||||
], 404);
|
||||
}
|
||||
|
||||
// Decode data form yang ada
|
||||
$dataForm = json_decode($inspeksi->data_form, true) ?: [];
|
||||
|
||||
// Inisialisasi array signature jika belum ada
|
||||
if (!isset($dataForm['signature'])) {
|
||||
$dataForm['signature'] = [];
|
||||
}
|
||||
|
||||
// Simpan atau update signature berdasarkan type
|
||||
$dataForm['signature'][$request->type] = [
|
||||
'image' => $request->signature,
|
||||
'created_at' => now()->toDateTimeString(),
|
||||
'updated_at' => now()->toDateTimeString()
|
||||
];
|
||||
|
||||
// Update data form di database
|
||||
$inspeksi->data_form = json_encode($dataForm);
|
||||
$inspeksi->save();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Tanda tangan berhasil disimpan'
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Terjadi kesalahan: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
public function signatureShow($type)
|
||||
{
|
||||
try {
|
||||
$inspeksi = Inspeksi::where('permohonan_id', request()->input('permohonan_id'))
|
||||
->where('dokument_id', request()->input('document_id'))
|
||||
->first();
|
||||
|
||||
if (!$inspeksi) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Data inspeksi tidak ditemukan',
|
||||
'error_code' => '404'
|
||||
], 404);
|
||||
}
|
||||
|
||||
$dataForm = json_decode($inspeksi->data_form, true) ?: [];
|
||||
|
||||
if (isset($dataForm['signature'][$type])) {
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'signature' => $dataForm['signature'][$type]['image'],
|
||||
'type' => $type,
|
||||
'created_at' => $dataForm['signature'][$type]['created_at'],
|
||||
'updated_at' => $dataForm['signature'][$type]['updated_at']
|
||||
]
|
||||
);
|
||||
]);
|
||||
}
|
||||
public function signatureShow()
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Tanda tangan tidak ditemukan',
|
||||
'error_code' => '404'
|
||||
], 404);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Terjadi kesalahan: ' . $e->getMessage(),
|
||||
'error_code' => '500'
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function signatureDestroy(Request $request)
|
||||
{
|
||||
try {
|
||||
$validator = Validator::make($request->all(), [
|
||||
'type' => 'required|in:penilai,cabang,debitur,kjjp'
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Validation error',
|
||||
'errors' => $validator->errors()
|
||||
], 422);
|
||||
}
|
||||
public function signatureDestroy()
|
||||
|
||||
$inspeksi = Inspeksi::where('permohonan_id', $request->input('permohonan_id'))
|
||||
->where('dokument_id', $request->input('document_id'))
|
||||
->first();
|
||||
|
||||
if (!$inspeksi) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Data inspeksi tidak ditemukan',
|
||||
'error_code' => '404'
|
||||
], 404);
|
||||
}
|
||||
|
||||
$dataForm = json_decode($inspeksi->data_form, true) ?: [];
|
||||
|
||||
if (isset($dataForm['signature'][$request->type])) {
|
||||
unset($dataForm['signature'][$request->type]);
|
||||
|
||||
$inspeksi->data_form = json_encode($dataForm);
|
||||
$inspeksi->save();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Tanda tangan berhasil dihapus',
|
||||
'data' => [
|
||||
'type' => $request->type,
|
||||
'deleted_at' => now()->toDateTimeString()
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Tanda tangan tidak ditemukan',
|
||||
'error_code' => '404'
|
||||
], 404);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Terjadi kesalahan: ' . $e->getMessage(),
|
||||
'error_code' => '500'
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
private function arrayMergeRecursive($arr1, $arr2)
|
||||
{
|
||||
|
||||
foreach ($arr2 as $key => $value) {
|
||||
if (is_array($value) && isset($arr1[$key]) && is_array($arr1[$key])) {
|
||||
$arr1[$key] = $this->arrayMergeRecursive($arr1[$key], $value);
|
||||
} else {
|
||||
// Jika nilai baru adalah null, pertahankan nilai lama
|
||||
if (!is_null($value)) {
|
||||
$arr1[$key] = $value;
|
||||
} elseif (!isset($arr1[$key])) {
|
||||
$arr1[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $arr1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
<h3 class="signature-title">{{ ucfirst($type) }}</h3>
|
||||
<canvas id="signature-pad-{{ $type }}" class="signature-pad" width="400" height="200"></canvas>
|
||||
<div class="button-container py-2">
|
||||
<button type="button" id="save-{{ $type }}" class="btn btn-xs btn-primary">Simpan</button>
|
||||
<button type="button" id="clear-{{ $type }}" class="btn btn-xs btn-danger">Hapus</button>
|
||||
<button type="button" id="save-{{ $type }}" class="btn btn-xs btn-primary">Save</button>
|
||||
<button type="button" id="clear-{{ $type }}" class="btn btn-xs btn-secondary">Clear</button>
|
||||
<button type="button" id="delete-{{$type}}" class="btn btn-xs btn-danger">Delete</button>
|
||||
</div>
|
||||
<div id="status-{{ $type }}" class="status-message"></div>
|
||||
</div>
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
<div class="w-full grid gap-5 lg:gap-7.5 mx-auto">
|
||||
<form id="formInspeksi" method="POST" enctype="multipart/form-data" class="grid gap-5">
|
||||
@csrf
|
||||
<input type="hidden" name="permohonan_id" value="{{ $permohonan->id }}">
|
||||
<input type="hidden" name="dokument_id" value="{{ request('dokument') }}">
|
||||
<input id="permohonan_id" type="hidden" name="permohonan_id" value="{{ $permohonan->id }}">
|
||||
<input id="dokument_id" type="hidden" name="dokument_id" value="{{ request('dokument') }}">
|
||||
<input type="hidden" name="nomor_registrasi" value="{{ $permohonan->nomor_registrasi }}">
|
||||
|
||||
@if (strtolower($permohonan->tujuanPenilaian->name) == 'rap')
|
||||
@@ -46,13 +46,25 @@
|
||||
@endif
|
||||
@endforeach
|
||||
@endif
|
||||
<div class="flex justify-end gap-2" style="margin-right: 20px; margin-top: 20px">
|
||||
<button type="button" class="btn btn-success" id="saveButton" onclick="submitData()">
|
||||
<span id="saveButtonText">Save</span>
|
||||
<div class="spinner-border spinner-border-sm text-light" role="status" style="display: none;"
|
||||
id="saveButtonSpinner">
|
||||
<div class="card border border-agi-100 w-full rounded-lg shadow-md overflow-hidden">
|
||||
<div class="card-header bg-agi-50">
|
||||
<h3 class="card-title uppercase">
|
||||
Tanda Tangan
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="flex items-baseline justify-between flex-wrap lg:flex-nowrap">
|
||||
@foreach (['penilai', 'cabang', 'debitur', 'kjjp'] as $type)
|
||||
@include('lpj::component.signature-pad', ['type' => $type])
|
||||
@endforeach
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end gap-2" style="margin-right: 20px; margin-top: 20px">
|
||||
<button type="button" class="btn btn-primary" id="saveButton" onclick="submitData()">
|
||||
<i class="ki-filled ki-save-2"></i>
|
||||
<span id="saveButtonText">Save</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -60,7 +72,226 @@
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script src="https://cdn.jsdelivr.net/npm/signature_pad@4.1.7/dist/signature_pad.umd.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const signaturePads = {};
|
||||
const types = ['penilai', 'cabang', 'debitur', 'kjjp'];
|
||||
|
||||
// Inisialisasi semua signature pad
|
||||
types.forEach(type => {
|
||||
initSignaturePad(type);
|
||||
});
|
||||
|
||||
function initSignaturePad(type) {
|
||||
const canvas = document.getElementById(`signature-pad-${type}`);
|
||||
if (!canvas) return;
|
||||
|
||||
// Set ukuran canvas yang responsif
|
||||
canvas.width = canvas.offsetWidth;
|
||||
canvas.height = canvas.offsetHeight;
|
||||
|
||||
signaturePads[type] = new SignaturePad(canvas, {
|
||||
backgroundColor: 'rgba(255, 255, 255, 0)',
|
||||
penColor: 'rgb(0, 0, 0)',
|
||||
minWidth: 0.5,
|
||||
maxWidth: 2.5
|
||||
});
|
||||
|
||||
// Load existing signature
|
||||
loadSignature(type);
|
||||
|
||||
// Event listeners
|
||||
const saveBtn = document.getElementById(`save-${type}`);
|
||||
const clearBtn = document.getElementById(`clear-${type}`);
|
||||
const deleteBtn = document.getElementById(`delete-${type}`);
|
||||
|
||||
if (saveBtn) {
|
||||
saveBtn.addEventListener('click', () => saveSignature(type));
|
||||
}
|
||||
if (clearBtn) {
|
||||
clearBtn.addEventListener('click', () => clearSignature(type));
|
||||
}
|
||||
if (deleteBtn) {
|
||||
deleteBtn.addEventListener('click', () => deleteSignature(type));
|
||||
}
|
||||
}
|
||||
|
||||
function saveSignature(type) {
|
||||
const signaturePad = signaturePads[type];
|
||||
|
||||
if (!signaturePad || signaturePad.isEmpty()) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Peringatan',
|
||||
text: 'Harap memberikan tanda tangan terlebih dahulu.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const data = {
|
||||
signature: signaturePad.toDataURL('image/png'),
|
||||
type: type,
|
||||
document_id: document.getElementById('dokument_id')?.value,
|
||||
permohonan_id: document.getElementById('permohonan_id')?.value
|
||||
};
|
||||
console.log(data);
|
||||
|
||||
|
||||
// Tampilkan loading
|
||||
Swal.fire({
|
||||
title: 'Menyimpan...',
|
||||
allowOutsideClick: false,
|
||||
didOpen: () => {
|
||||
Swal.showLoading();
|
||||
}
|
||||
});
|
||||
|
||||
fetch(`{{ url('/surveyor/signatures') }}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
Swal.close();
|
||||
if (data.success) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
text: 'Tanda tangan berhasil disimpan!',
|
||||
timer: 1500
|
||||
});
|
||||
|
||||
} else {
|
||||
throw new Error(data.message || 'Terjadi kesalahan');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: error.message || 'Terjadi kesalahan saat menyimpan tanda tangan'
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function loadSignature(type) {
|
||||
const params = new URLSearchParams({
|
||||
document_id: document.getElementById('dokument_id')?.value,
|
||||
permohonan_id: document.getElementById('permohonan_id')?.value
|
||||
});
|
||||
|
||||
fetch(`{{ url('/surveyor/signatures/${type}?${params}') }}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success && data.data?.signature) {
|
||||
const canvas = document.getElementById(`signature-pad-${type}`);
|
||||
if (!canvas) return;
|
||||
|
||||
const signaturePad = signaturePads[type];
|
||||
const image = new Image();
|
||||
|
||||
image.onload = function() {
|
||||
const context = canvas.getContext('2d');
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
||||
};
|
||||
image.src = data.data.signature;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error loading signature:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function deleteSignature(type) {
|
||||
Swal.fire({
|
||||
title: 'Konfirmasi',
|
||||
text: 'Apakah Anda yakin ingin menghapus tanda tangan ini?',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Ya, Hapus',
|
||||
cancelButtonText: 'Batal'
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
const data = {
|
||||
type: type,
|
||||
document_id: document.getElementById('dokument_id')?.value,
|
||||
permohonan_id: document.getElementById('permohonan_id')?.value
|
||||
};
|
||||
|
||||
fetch(`{{ url('/surveyor/signatures/${type}') }}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
clearSignature(type);
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
text: 'Tanda tangan berhasil dihapus!',
|
||||
timer: 1500
|
||||
});
|
||||
} else {
|
||||
throw new Error(data.message || 'Terjadi kesalahan');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: error.message ||
|
||||
'Terjadi kesalahan saat menghapus tanda tangan'
|
||||
});
|
||||
console.error('Error:', error);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function clearSignature(type) {
|
||||
const signaturePad = signaturePads[type];
|
||||
if (signaturePad) {
|
||||
signaturePad.clear();
|
||||
updateStatus(type, '');
|
||||
}
|
||||
}
|
||||
|
||||
function updateStatus(type, message, status = '') {
|
||||
const statusElement = document.getElementById(`status-${type}`);
|
||||
if (statusElement) {
|
||||
statusElement.textContent = message;
|
||||
statusElement.className = `status-message ${status}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle window resize untuk canvas responsif
|
||||
window.addEventListener('resize', function() {
|
||||
types.forEach(type => {
|
||||
const canvas = document.getElementById(`signature-pad-${type}`);
|
||||
if (canvas) {
|
||||
const ratio = Math.max(window.devicePixelRatio || 1, 1);
|
||||
canvas.width = canvas.offsetWidth * ratio;
|
||||
canvas.height = canvas.offsetHeight * ratio;
|
||||
canvas.getContext('2d').scale(ratio, ratio);
|
||||
|
||||
// Reload signature jika ada
|
||||
loadSignature(type);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function updateAlamatFields(status) {
|
||||
|
||||
@@ -557,6 +557,12 @@ Route::middleware(['auth'])->group(function () {
|
||||
Route::get('data-pembanding/{id}/create', [SurveyorController::class, 'dataPembanding'])->name('data-pembanding');
|
||||
Route::post('submitSurveyor/{id}', [SurveyorController::class, 'submitSurveyor'])->name('submitSurveyor');
|
||||
Route::post('update_analisa/{id}', [SurveyorController::class, 'update_analisa'])->name('update_analisa');
|
||||
|
||||
Route::get('/signatures', [SurveyorController::class, 'signatures'])->name('signatures');
|
||||
Route::post('/signatures', [SurveyorController::class, 'signatureStore'])->name('signatureStore');
|
||||
Route::get('/signatures/{type}', [SurveyorController::class, 'signatureShow'])->name('signatureShow');
|
||||
Route::delete('/signatures/{type}', [SurveyorController::class, 'signatureDestroy'])->name('signatureDestroy');
|
||||
|
||||
});
|
||||
|
||||
Route::name('penilai.')->prefix('penilai')->group(function () {
|
||||
|
||||
Reference in New Issue
Block a user