This commit is contained in:
2026-01-18 21:16:53 +08:00
parent c5abd68e9e
commit 1cd7db0fcc
11 changed files with 1424 additions and 136 deletions

5
admin/api/config_api.php Normal file
View File

@@ -0,0 +1,5 @@
<?php
// Google Gemini API Configuration
define('GEMINI_API_KEY', 'AIzaSyDp9crq4QWN15xBXbDY2FBXdUoRg1LgM1M');
define('GEMINI_API_URL', 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent');
?>

95
admin/api/ocr_helper.php Normal file
View File

@@ -0,0 +1,95 @@
<?php
header('Content-Type: application/json');
require_once 'config_api.php';
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
echo json_encode(['success' => false, 'message' => 'Invalid request method']);
exit;
}
if (!isset($_FILES['image']) || $_FILES['image']['error'] !== UPLOAD_ERR_OK) {
echo json_encode(['success' => false, 'message' => 'Upload failed or no image provided']);
exit;
}
$type = $_POST['type'] ?? 'ktp'; // 'ktp' or 'kk'
$imagePath = $_FILES['image']['tmp_name'];
$imageData = base64_encode(file_get_contents($imagePath));
$mimeType = mime_content_type($imagePath);
// Construct Prompt based on Type
if ($type === 'kk') {
$promptText = "Extract strictly from this Indonesian Family Card (Kartu Keluarga). Return ONLY a raw JSON object with these keys: 'no_kk' (16 digits), 'kepala_keluarga' (Name), 'alamat', 'rt', 'rw', 'desa', 'kecamatan', 'kabupaten', 'provinsi', 'kode_pos', 'anggota': [ { 'nik': '...', 'nama': '...', 'hubungan': '...' } ] (Array of all family members found in the table. 'hubungan' examples: KEPALA KELUARGA, ISTRI, ANAK, FAMILI LAIN). Value must be string. If not found, use empty string.";
} else {
// Default to KTP
$promptText = "Extract data from this Indonesian KTP. Return ONLY a raw JSON object with keys: 'nik' (16 digits), 'nama' (Name), 'tempat_lh' (Place of Birth), 'tgl_lh' (YYYY-MM-DD), 'je_kel' (LK/PR), 'alamat' (Street only), 'rt', 'rw', 'desa' (Kel/Desa), 'kecamatan', 'kabupaten' (or Kota), 'provinsi', 'agama', 'status' (Sudah/Belum/Cerai Hidup/Cerai Mati), 'pekerjaan', 'kewarganegaraan' (WNI/WNA). Clean up text. If field is unclear, return empty string.";
}
// Payload for Gemini
$data = [
"contents" => [
[
"parts" => [
["text" => $promptText],
[
"inline_data" => [
"mime_type" => $mimeType,
"data" => $imageData
]
]
]
]
]
];
// Send Request
$ch = curl_init(GEMINI_API_URL . '?key=' . GEMINI_API_KEY);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
echo json_encode(['success' => false, 'message' => 'CURL Error: ' . $error]);
exit;
}
$result = json_decode($response, true);
// Debugging check
if (isset($result['error'])) {
echo json_encode(['success' => false, 'message' => 'API Error: ' . $result['error']['message']]);
exit;
}
if (!isset($result['candidates'][0]['content']['parts'][0]['text'])) {
echo json_encode(['success' => false, 'message' => 'No text returned from AI', 'raw' => $result]);
exit;
}
// Parse AI Response
$rawText = $result['candidates'][0]['content']['parts'][0]['text'];
// Remove Markdown Code Blocks if any (```json ... ```)
$cleanJson = preg_replace('/^```json\s*|\s*```$/', '', trim($rawText));
$parsedData = json_decode($cleanJson, true);
if (json_last_error() !== JSON_ERROR_NONE) {
// Fallback: try to find JSON object structure in text
if (preg_match('/\{.*\}/s', $cleanJson, $matches)) {
$parsedData = json_decode($matches[0], true);
}
}
if (!$parsedData) {
echo json_encode(['success' => false, 'message' => 'Failed to parse AI response', 'raw_text' => $rawText]);
exit;
}
echo json_encode(['success' => true, 'data' => $parsedData]);
?>

View File

@@ -6,10 +6,49 @@
<form action="" method="post" enctype="multipart/form-data">
<div class="card-body">
<div class="form-group row">
<label class="col-sm-2 col-form-label">Foto KK</label>
<div class="col-sm-6">
<img id="preview_kk" src="#" alt="Preview Foto" class="img-fluid mb-2" style="display:none; max-height: 300px; border: 1px solid #ddd; border-radius: 5px;">
<input type="file" class="form-control mb-2" id="foto_kk" name="foto_kk" accept=".jpg, .jpeg, .png">
<button type="button" class="btn btn-success btn-block" id="btnScanKK">
<i class="fas fa-magic"></i> Scan KK dengan AI
</button>
<small class="text-muted">Pilih foto, crop, lalu scan.</small>
<input type="hidden" id="foto_cropped" name="foto_cropped">
</div>
</div>
<!-- Modal Crop -->
<div class="modal fade" id="modalCrop" tabindex="-1" role="dialog" aria-labelledby="modalCropLabel" aria-hidden="true">
<div class="modal-dialog modal-xl" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalCropLabel">Potong & Putar Foto KK</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="img-container" style="max-height: 500px;">
<img id="image-to-crop" src="" style="max-width: 100%; display: block;">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" id="btnRotateLeft" title="Putar Kiri"><i class="fas fa-undo"></i></button>
<button type="button" class="btn btn-secondary" id="btnRotateRight" title="Putar Kanan"><i class="fas fa-redo"></i></button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
<button type="button" class="btn btn-primary" id="btnCrop">Potong & Simpan</button>
</div>
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">No KK</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="no_kk" name="no_kk" placeholder="No KK" required>
<input type="hidden" id="anggota_json" name="anggota_json">
</div>
</div>
@@ -58,12 +97,7 @@
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Foto KK</label>
<div class="col-sm-6">
<input type="file" class="form-control" id="foto_kk" name="foto_kk">
</div>
</div>
</div>
<div class="card-footer">
@@ -73,27 +107,250 @@
</form>
</div>
<script>
window.addEventListener('load', function() {
// Cropper Logic
var cropper;
var image = document.getElementById('image-to-crop');
var inputImage = document.getElementById('foto_kk');
var modal = $('#modalCrop');
inputImage.addEventListener('change', function(e) {
var files = e.target.files;
var done = function(url) {
inputImage.value = ''; // Clear input
image.src = url;
modal.modal('show');
};
var reader;
var file;
var url;
if (files && files.length > 0) {
file = files[0];
if (URL) {
done(URL.createObjectURL(file));
} else if (FileReader) {
reader = new FileReader();
reader.onload = function(e) {
done(reader.result);
};
reader.readAsDataURL(file);
}
}
});
modal.on('shown.bs.modal', function() {
cropper = new Cropper(image, {
aspectRatio: NaN, // Free Ratio for KK
viewMode: 1,
autoCropArea: 1,
});
}).on('hidden.bs.modal', function() {
cropper.destroy();
cropper = null;
});
document.getElementById('btnRotateLeft').addEventListener('click', function() {
cropper.rotate(-90);
});
document.getElementById('btnRotateRight').addEventListener('click', function() {
cropper.rotate(90);
});
document.getElementById('btnCrop').addEventListener('click', function() {
var canvas;
if (cropper) {
canvas = cropper.getCroppedCanvas({ width: 1200 }); // Larger for KK
var base64 = canvas.toDataURL('image/jpeg');
var output = document.getElementById('preview_kk');
output.src = base64;
output.style.display = 'block';
document.getElementById('foto_cropped').value = base64;
modal.modal('hide');
}
});
// Scan
document.getElementById('btnScanKK').addEventListener('click', function() {
var fileInput = document.getElementById('foto_kk');
var croppedVal = document.getElementById('foto_cropped').value;
if(fileInput.files.length === 0 && !croppedVal) {
Swal.fire('Info', 'Silakan pilih foto KK terlebih dahulu.', 'warning');
return;
}
var btn = this;
var originalText = btn.innerHTML;
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Menganalisa...';
btn.disabled = true;
var formData = new FormData();
var croppedData = document.getElementById('foto_cropped').value;
if (croppedData) {
var byteString = atob(croppedData.split(',')[1]);
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
var blob = new Blob([ab], { type: 'image/jpeg' });
formData.append('image', blob, 'cropped_kk.jpg');
} else if (fileInput.files.length > 0) {
formData.append('image', fileInput.files[0]);
} else {
Swal.fire('Info', 'Belum ada foto yang dipilih / dicrop.', 'warning');
btn.disabled = false;
btn.innerHTML = originalText;
return;
}
formData.append('type', 'kk');
fetch('admin/api/ocr_helper.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if(data.success) {
var d = data.data;
// Show Confirmation
Swal.fire({
title: 'Hasil Scan KK',
html: `
<div style="text-align: left; font-size: 0.9rem;">
<table class="table table-bordered table-sm">
<tr><td width="30%">No KK</td><td><b>${d.no_kk || '-'}</b></td></tr>
<tr><td>Kepala Kel</td><td><b>${d.kepala_keluarga || '-'}</b></td></tr>
<tr><td>Alamat</td><td>${d.desa || '-'}</td></tr>
<tr><td>RT/RW</td><td>${d.rt || '-'}/${d.rw || '-'}</td></tr>
<tr><td>Kecamatan</td><td>${d.kecamatan || '-'}</td></tr>
<tr><td>Kabupaten</td><td>${d.kabupaten || '-'}</td></tr>
<tr><td>Provinsi</td><td>${d.provinsi || '-'}</td></tr>
</table>
<p class="mb-0 text-muted">Gunakan data ini?</p>
</div>
`,
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Ya, Gunakan',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.value) {
if(d.no_kk) document.getElementsByName('no_kk')[0].value = d.no_kk;
if(d.anggota) document.getElementById('anggota_json').value = JSON.stringify(d.anggota);
if(d.kepala_keluarga) document.getElementsByName('kepala')[0].value = d.kepala_keluarga;
if(d.desa) document.getElementsByName('desa')[0].value = d.desa;
if(d.rt) document.getElementsByName('rt')[0].value = d.rt;
if(d.rw) document.getElementsByName('rw')[0].value = d.rw;
if(d.kecamatan) document.getElementsByName('kec')[0].value = d.kecamatan;
if(d.kabupaten) document.getElementsByName('kab')[0].value = d.kabupaten;
if(d.provinsi) document.getElementsByName('prov')[0].value = d.provinsi;
Swal.fire('Berhasil!', 'Data KK telah masuk ke form.', 'success');
}
});
} else {
Swal.fire('Gagal', data.message, 'error');
}
})
.catch(err => {
console.error(err);
Swal.fire('Error', 'Terjadi kesalahan: ' + err.message, 'error');
})
.finally(() => {
btn.innerHTML = originalText;
btn.disabled = false;
});
});
});
</script>
<?php
if (isset ($_POST['Simpan'])){
$sumber = @$_FILES['foto_kk']['tmp_name'];
$target = 'foto/kk/';
$nama_file = @$_FILES['foto_kk']['name'];
$pindah = move_uploaded_file($sumber, $target.$nama_file);
// Cek Crop
if (!empty($_POST['foto_cropped'])) {
$data = $_POST['foto_cropped'];
$parts = explode(',', $data);
$data = $parts[1];
$data = base64_decode($data);
$nama_file = "KK-" . time() . ".jpg";
file_put_contents($target . $nama_file, $data);
} else {
$sumber = @$_FILES['foto_kk']['tmp_name'];
if(!empty($sumber)) {
move_uploaded_file($sumber, $target.$nama_file);
}
}
//mulai proses simpan data
$no_kk = $_POST['no_kk'];
$cek_kk = mysqli_query($koneksi, "SELECT * FROM tb_kk WHERE no_kk='$no_kk'");
if(mysqli_num_rows($cek_kk) > 0){
echo "<script>
Swal.fire({title: 'Gagal',text: 'No KK sudah terdaftar dalam sistem!',icon: 'error',confirmButtonText: 'OK'
}).then((result) => {if (result.value){
window.location = 'index.php?page=add-kartu';
}
})</script>";
return;
}
// Sanitize Inputs
$no_kk = mysqli_real_escape_string($koneksi, $_POST['no_kk']);
$kepala = mysqli_real_escape_string($koneksi, $_POST['kepala']);
$desa = mysqli_real_escape_string($koneksi, $_POST['desa']);
$rt = mysqli_real_escape_string($koneksi, $_POST['rt']);
$rw = mysqli_real_escape_string($koneksi, $_POST['rw']);
$kec = mysqli_real_escape_string($koneksi, $_POST['kec']);
$kab = mysqli_real_escape_string($koneksi, $_POST['kab']);
$prov = mysqli_real_escape_string($koneksi, $_POST['prov']);
$sql_simpan = "INSERT INTO tb_kk (no_kk, kepala, desa, rt, rw, kec, kab, prov, foto_kk) VALUES (
'".$_POST['no_kk']."',
'".$_POST['kepala']."',
'".$_POST['desa']."',
'".$_POST['rt']."',
'".$_POST['rw']."',
'".$_POST['kec']."',
'".$_POST['kab']."',
'".$_POST['prov']."',
'".$nama_file."')";
'$no_kk',
'$kepala',
'$desa',
'$rt',
'$rw',
'$kec',
'$kab',
'$prov',
'$nama_file')";
$query_simpan = mysqli_query($koneksi, $sql_simpan);
// Process Auto-Linking Members
if ($query_simpan && !empty($_POST['anggota_json'])) {
$id_kk_baru = mysqli_insert_id($koneksi);
$anggota_list = json_decode($_POST['anggota_json'], true);
if (is_array($anggota_list)) {
foreach ($anggota_list as $mem) {
$nik_mem = mysqli_real_escape_string($koneksi, $mem['nik']);
$hub_mem = mysqli_real_escape_string($koneksi, $mem['hubungan']);
// Search Resident by NIK
$sql_cek_pend = "SELECT id_pend FROM tb_pdd WHERE nik='$nik_mem'";
$q_cek_pend = mysqli_query($koneksi, $sql_cek_pend);
if ($row_pend = mysqli_fetch_assoc($q_cek_pend)) {
$id_pend_found = $row_pend['id_pend'];
// Insert into tb_anggota
$sql_add_ang = "INSERT INTO tb_anggota (id_kk, id_pend, hubungan) VALUES ('$id_kk_baru', '$id_pend_found', '$hub_mem')";
mysqli_query($koneksi, $sql_add_ang);
}
}
}
}
mysqli_close($koneksi);
if ($query_simpan) {

View File

@@ -56,8 +56,7 @@
<a href="?page=edit-kartu&kode=<?php echo $data['id_kk']; ?>" title="Ubah" class="btn btn-success btn-sm">
<i class="fa fa-edit"></i>
</a>
<a href="?page=del-kartu&kode=<?php echo $data['id_kk']; ?>" onclick="return confirm('Apakah anda yakin hapus data ini ?')"
title="Hapus" class="btn btn-danger btn-sm">
<a href="#" onclick="HapusData('<?php echo $data['id_kk']; ?>')" title="Hapus" class="btn btn-danger btn-sm">
<i class="fa fa-trash"></i>
</a>
</td>
@@ -71,4 +70,24 @@
</table>
</div>
</div>
<!-- /.card-body -->
</div>
<!-- /.card-body -->
<script>
function HapusData(id){
Swal.fire({
title: 'Apakah anda yakin?',
text: "Data yang dihapus tidak dapat dikembalikan!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Ya, Hapus!',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.value) {
window.location.href = "?page=del-kartu&kode=" + id;
}
})
}
</script>

View File

@@ -15,6 +15,49 @@
<form action="" method="post" enctype="multipart/form-data">
<div class="card-body">
<div class="form-group row">
<label class="col-sm-2 col-form-label">Foto KK</label>
<div class="col-sm-6">
<?php if(!empty($data_cek['foto_kk'])): ?>
<img id="preview_kk" src="foto/kk/<?php echo $data_cek['foto_kk']; ?>" width="200px" style="border: 2px solid #6c757d; padding: 3px; border-radius: 5px; cursor: pointer; display: block;" class="mb-2">
<?php else: ?>
<img id="preview_kk" src="dist/img/noimage.png" width="200px" style="border: 2px solid #6c757d; padding: 3px; border-radius: 5px; display: block;" class="mb-2">
<?php endif; ?>
<input type="file" class="form-control mb-2" id="foto_kk" name="foto_kk" accept=".jpg, .jpeg, .png">
<button type="button" class="btn btn-success btn-block" id="btnScanKK">
<i class="fas fa-magic"></i> Scan Ulang dengan AI
</button>
<small class="text-muted">Pilih foto, crop, lalu scan.</small>
<input type="hidden" id="foto_cropped" name="foto_cropped">
</div>
</div>
<!-- Modal Crop -->
<div class="modal fade" id="modalCrop" tabindex="-1" role="dialog" aria-labelledby="modalCropLabel" aria-hidden="true">
<div class="modal-dialog modal-xl" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalCropLabel">Potong & Putar Foto KK</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="img-container" style="max-height: 500px;">
<img id="image-to-crop" src="" style="max-width: 100%; display: block;">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" id="btnRotateLeft" title="Putar Kiri"><i class="fas fa-undo"></i></button>
<button type="button" class="btn btn-secondary" id="btnRotateRight" title="Putar Kanan"><i class="fas fa-redo"></i></button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
<button type="button" class="btn btn-primary" id="btnCrop">Potong & Simpan</button>
</div>
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">No Sistem</label>
<div class="col-sm-3">
@@ -84,23 +127,7 @@
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Foto KK</label>
<div class="col-sm-6">
<?php if(!empty($data_cek['foto_kk'])): ?>
<img src="foto/kk/<?php echo $data_cek['foto_kk']; ?>" width="200px" style="border: 2px solid #6c757d; padding: 3px; border-radius: 5px; cursor: pointer;" onclick="showImage(this.src)" title="Klik untuk memperbesar">
<?php else: ?>
<img src="dist/img/noimage.png" width="200px" style="border: 2px solid #6c757d; padding: 3px; border-radius: 5px;">
<?php endif; ?>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Ubah Foto</label>
<div class="col-sm-6">
<input type="file" class="form-control" id="foto_kk" name="foto_kk">
</div>
</div>
</div>
<div class="card-footer">
@@ -110,46 +137,234 @@
</form>
</div>
<script>
window.addEventListener('load', function() {
// Cropper Logic
var cropper;
var image = document.getElementById('image-to-crop');
var inputImage = document.getElementById('foto_kk');
var modal = $('#modalCrop');
inputImage.addEventListener('change', function(e) {
var files = e.target.files;
var done = function(url) {
inputImage.value = '';
image.src = url;
modal.modal('show');
};
var reader;
var file;
var url;
if (files && files.length > 0) {
file = files[0];
if (URL) {
done(URL.createObjectURL(file));
} else if (FileReader) {
reader = new FileReader();
reader.onload = function(e) {
done(reader.result);
};
reader.readAsDataURL(file);
}
}
});
modal.on('shown.bs.modal', function() {
cropper = new Cropper(image, {
aspectRatio: NaN,
viewMode: 1,
autoCropArea: 1,
});
}).on('hidden.bs.modal', function() {
cropper.destroy();
cropper = null;
});
document.getElementById('btnRotateLeft').addEventListener('click', function() {
cropper.rotate(-90);
});
document.getElementById('btnRotateRight').addEventListener('click', function() {
cropper.rotate(90);
});
document.getElementById('btnCrop').addEventListener('click', function() {
var canvas;
if (cropper) {
canvas = cropper.getCroppedCanvas({ width: 1200 });
var base64 = canvas.toDataURL('image/jpeg');
var output = document.getElementById('preview_kk');
output.src = base64;
// output.style.display = 'block'; // Already block in View? Check img tag
document.getElementById('foto_cropped').value = base64;
modal.modal('hide');
}
});
// Scan
document.getElementById('btnScanKK').addEventListener('click', function() {
var fileInput = document.getElementById('foto_kk');
var croppedVal = document.getElementById('foto_cropped').value;
if(fileInput.files.length === 0 && !croppedVal) {
Swal.fire('Info', 'Silakan pilih foto KK baru terlebih dahulu.', 'warning');
return;
}
var btn = this;
var originalText = btn.innerHTML;
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Menganalisa...';
btn.disabled = true;
var formData = new FormData();
var croppedData = document.getElementById('foto_cropped').value;
if (croppedData) {
var byteString = atob(croppedData.split(',')[1]);
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
var blob = new Blob([ab], { type: 'image/jpeg' });
formData.append('image', blob, 'cropped_kk_edit.jpg');
} else if (fileInput.files.length > 0) {
formData.append('image', fileInput.files[0]);
} else {
Swal.fire('Info', 'Belum ada foto baru yang dipilih / dicrop.', 'warning');
btn.disabled = false;
btn.innerHTML = originalText;
return;
}
formData.append('type', 'kk');
fetch('admin/api/ocr_helper.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if(data.success) {
var d = data.data;
// Show Confirmation
Swal.fire({
title: 'Hasil Scan KK',
html: `
<div style="text-align: left; font-size: 0.9rem;">
<table class="table table-bordered table-sm">
<tr><td width="30%">No KK</td><td><b>${d.no_kk || '-'}</b></td></tr>
<tr><td>Kepala Kel</td><td><b>${d.kepala_keluarga || '-'}</b></td></tr>
<tr><td>Alamat</td><td>${d.desa || '-'}</td></tr>
<tr><td>RT/RW</td><td>${d.rt || '-'}/${d.rw || '-'}</td></tr>
<tr><td>Kecamatan</td><td>${d.kecamatan || '-'}</td></tr>
<tr><td>Kabupaten</td><td>${d.kabupaten || '-'}</td></tr>
<tr><td>Provinsi</td><td>${d.provinsi || '-'}</td></tr>
</table>
<p class="mb-0 text-muted">Perbarui data formulir?</p>
</div>
`,
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Ya, Update',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.value) {
if(d.no_kk) document.getElementsByName('no_kk')[0].value = d.no_kk;
if(d.kepala_keluarga) document.getElementsByName('kepala')[0].value = d.kepala_keluarga;
if(d.desa) document.getElementsByName('desa')[0].value = d.desa;
if(d.rt) document.getElementsByName('rt')[0].value = d.rt;
if(d.rw) document.getElementsByName('rw')[0].value = d.rw;
if(d.kecamatan) document.getElementsByName('kec')[0].value = d.kecamatan;
if(d.kabupaten) document.getElementsByName('kab')[0].value = d.kabupaten;
if(d.provinsi) document.getElementsByName('prov')[0].value = d.provinsi;
Swal.fire('Berhasil!', 'Data formulir telah diperbarui.', 'success');
}
});
} else {
Swal.fire('Gagal', data.message, 'error');
}
})
.catch(err => {
console.error(err);
Swal.fire('Error', 'Terjadi kesalahan: ' + err.message, 'error');
})
.finally(() => {
btn.innerHTML = originalText;
btn.disabled = false;
});
});
});
</script>
<?php
if (isset ($_POST['Ubah'])){
$sumber = @$_FILES['foto_kk']['tmp_name'];
$target = 'foto/kk/';
$nama_file = @$_FILES['foto_kk']['name'];
$pindah = move_uploaded_file($sumber, $target.$nama_file);
$sumber = @$_FILES['foto_kk']['tmp_name'];
$has_new_photo = false;
// Cek Crop
if (!empty($_POST['foto_cropped'])) {
$data = $_POST['foto_cropped'];
$parts = explode(',', $data);
$data = $parts[1];
$data = base64_decode($data);
$nama_file = "KK-" . time() . ".jpg";
file_put_contents($target . $nama_file, $data);
$has_new_photo = true;
} elseif (!empty($sumber)) {
move_uploaded_file($sumber, $target.$nama_file);
$has_new_photo = true;
}
if(!empty($sumber)){
// Sanitize Inputs
$id_kk = mysqli_real_escape_string($koneksi, $_POST['id_kk']);
$no_kk = mysqli_real_escape_string($koneksi, $_POST['no_kk']);
$kepala = mysqli_real_escape_string($koneksi, $_POST['kepala']);
$desa = mysqli_real_escape_string($koneksi, $_POST['desa']);
$rt = mysqli_real_escape_string($koneksi, $_POST['rt']);
$rw = mysqli_real_escape_string($koneksi, $_POST['rw']);
$kec = mysqli_real_escape_string($koneksi, $_POST['kec']);
$kab = mysqli_real_escape_string($koneksi, $_POST['kab']);
$prov = mysqli_real_escape_string($koneksi, $_POST['prov']);
if($has_new_photo){
$foto= $data_cek['foto_kk'];
if (file_exists("foto/kk/$foto")){
unlink("foto/kk/$foto");}
$sql_ubah = "UPDATE tb_kk SET
no_kk='".$_POST['no_kk']."',
kepala='".$_POST['kepala']."',
desa='".$_POST['desa']."',
rt='".$_POST['rt']."',
rw='".$_POST['rw']."',
kec='".$_POST['kec']."',
kab='".$_POST['kab']."',
prov='".$_POST['prov']."',
foto_kk='".$nama_file."'
WHERE id_kk='".$_POST['id_kk']."'";
no_kk='$no_kk',
kepala='$kepala',
desa='$desa',
rt='$rt',
rw='$rw',
kec='$kec',
kab='$kab',
prov='$prov',
foto_kk='$nama_file'
WHERE id_kk='$id_kk'";
$query_ubah = mysqli_query($koneksi, $sql_ubah);
}elseif(empty($sumber)){
}else{
$sql_ubah = "UPDATE tb_kk SET
no_kk='".$_POST['no_kk']."',
kepala='".$_POST['kepala']."',
desa='".$_POST['desa']."',
rt='".$_POST['rt']."',
rw='".$_POST['rw']."',
kec='".$_POST['kec']."',
kab='".$_POST['kab']."',
prov='".$_POST['prov']."'
WHERE id_kk='".$_POST['id_kk']."'";
no_kk='$no_kk',
kepala='$kepala',
desa='$desa',
rt='$rt',
rw='$rw',
kec='$kec',
kab='$kab',
prov='$prov'
WHERE id_kk='$id_kk'";
$query_ubah = mysqli_query($koneksi, $sql_ubah);
}
mysqli_close($koneksi);

View File

@@ -6,6 +6,44 @@
<form action="" method="post" enctype="multipart/form-data">
<div class="card-body">
<div class="form-group row">
<label class="col-sm-2 col-form-label">Foto KTP</label>
<div class="col-sm-6">
<img id="preview_ktp" src="#" alt="Preview Foto" class="img-fluid mb-2" style="display:none; max-height: 300px; border: 1px solid #ddd; border-radius: 5px;">
<input type="file" class="form-control mb-2" id="foto_ktp" name="foto_ktp" accept=".jpg, .jpeg, .png">
<button type="button" class="btn btn-info btn-block" id="btnScanKTP">
<i class="fas fa-magic"></i> Scan Foto dengan AI
</button>
<small class="text-muted">Pilih foto, crop, lalu scan.</small>
<input type="hidden" id="foto_cropped" name="foto_cropped">
</div>
</div>
<!-- Modal Crop -->
<div class="modal fade" id="modalCrop" tabindex="-1" role="dialog" aria-labelledby="modalCropLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalCropLabel">Potong & Putar Foto</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="img-container" style="max-height: 500px;">
<img id="image-to-crop" src="" style="max-width: 100%; display: block;">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" id="btnRotateLeft" title="Putar Kiri"><i class="fas fa-undo"></i></button>
<button type="button" class="btn btn-secondary" id="btnRotateRight" title="Putar Kanan"><i class="fas fa-redo"></i></button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
<button type="button" class="btn btn-primary" id="btnCrop">Potong & Simpan</button>
</div>
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">NIK</label>
<div class="col-sm-6">
@@ -21,7 +59,7 @@
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">TTL</label>
<label class="col-sm-2 col-form-label">Tempat/Tanggal Lahir</label>
<div class="col-sm-3">
<input type="text" class="form-control" id="tempat_lh" name="tempat_lh" placeholder="Tempat Lahir" required>
</div>
@@ -31,12 +69,13 @@
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Jenis Kelain</label>
<label class="col-sm-2 col-form-label">Jenis Kelamin</label>
<div class="col-sm-3">
<select name="jekel" id="jekel" class="form-control">
<option>- Pilih -</option>
<option>LK</option>
<option>PR</option>
<option>- Pilih -</option>
<option value="LK">LAKI-LAKI</option>
<option value="PR">PEREMPUAN</option>
</select>
</div>
</div>
@@ -48,6 +87,27 @@
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Kecamatan</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="kecamatan" name="kecamatan" placeholder="Kecamatan" required>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Kabupaten</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="kabupaten" name="kabupaten" placeholder="Kabupaten" required>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Provinsi</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="provinsi" name="provinsi" placeholder="Provinsi" required>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">RT/RW</label>
<div class="col-sm-3">
@@ -86,12 +146,14 @@
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Foto KTP</label>
<label class="col-sm-2 col-form-label">Kewarganegaraan</label>
<div class="col-sm-6">
<input type="file" class="form-control" id="foto_ktp" name="foto_ktp">
<input type="text" class="form-control" id="kewarganegaraan" name="kewarganegaraan" placeholder="WNI/WNA" required>
</div>
</div>
</div>
<div class="card-footer">
<input type="submit" name="Simpan" value="Simpan" class="btn btn-info">
@@ -100,30 +162,286 @@
</form>
</div>
<script>
window.addEventListener('load', function() {
// Cropper Logic
var cropper;
var image = document.getElementById('image-to-crop');
var inputImage = document.getElementById('foto_ktp');
var modal = $('#modalCrop');
inputImage.addEventListener('change', function(e) {
var files = e.target.files;
var done = function(url) {
inputImage.value = ''; // Clear input to avoid double submit issues if cancelled
image.src = url;
modal.modal('show');
};
var reader;
var file;
var url;
if (files && files.length > 0) {
file = files[0];
if (URL) {
done(URL.createObjectURL(file));
} else if (FileReader) {
reader = new FileReader();
reader.onload = function(e) {
done(reader.result);
};
reader.readAsDataURL(file);
}
}
});
modal.on('shown.bs.modal', function() {
cropper = new Cropper(image, {
aspectRatio: 1.58, // KTP Ratio
viewMode: 1,
autoCropArea: 1,
});
}).on('hidden.bs.modal', function() {
cropper.destroy();
cropper = null;
});
document.getElementById('btnRotateLeft').addEventListener('click', function() {
cropper.rotate(-90);
});
document.getElementById('btnRotateRight').addEventListener('click', function() {
cropper.rotate(90);
});
document.getElementById('btnCrop').addEventListener('click', function() {
var canvas;
if (cropper) {
canvas = cropper.getCroppedCanvas({
width: 1000,
});
var base64 = canvas.toDataURL('image/jpeg');
// Show Preview
var output = document.getElementById('preview_ktp');
output.src = base64;
output.style.display = 'block';
// Set Hidden Input
document.getElementById('foto_cropped').value = base64;
modal.modal('hide');
}
});
// Scan Button
document.getElementById('btnScanKTP').addEventListener('click', function() {
var fileInput = document.getElementById('foto_ktp');
var croppedVal = document.getElementById('foto_cropped').value;
if(fileInput.files.length === 0 && !croppedVal) {
Swal.fire('Info', 'Silakan pilih foto KTP terlebih dahulu.', 'warning');
return;
}
var btn = this;
var originalText = btn.innerHTML;
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Menganalisa...';
btn.disabled = true;
var formData = new FormData();
var croppedData = document.getElementById('foto_cropped').value;
if (croppedData) {
// Convert Base64 to Blob
var byteString = atob(croppedData.split(',')[1]);
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
var blob = new Blob([ab], { type: 'image/jpeg' });
formData.append('image', blob, 'cropped_ktp.jpg');
} else if (fileInput.files.length > 0) {
formData.append('image', fileInput.files[0]);
} else {
Swal.fire('Info', 'Belum ada foto yang dipilih / dicrop.', 'warning');
btn.disabled = false;
btn.innerHTML = originalText;
return;
}
formData.append('type', 'ktp');
fetch('admin/api/ocr_helper.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if(data.success) {
var d = data.data;
// Show Confirmation Dialog
Swal.fire({
title: 'Hasil Scan AI',
html: `
<div style="text-align: left; font-size: 0.9rem;">
<table class="table table-bordered table-sm">
<tr><td width="30%">NIK</td><td><b>${d.nik || '-'}</b></td></tr>
<tr><td>Nama</td><td><b>${d.nama || '-'}</b></td></tr>
<tr><td>TTL</td><td>${d.tempat_lh || '-'}, ${d.tgl_lh || '-'}</td></tr>
<tr><td>JK</td><td>${d.je_kel || '-'}</td></tr>
<tr><td>Alamat</td><td>${d.alamat || '-'}</td></tr>
<tr><td>RT/RW</td><td>${d.rt || '000'}/${d.rw || '000'}</td></tr>
<tr><td>Desa</td><td>${d.desa || '-'}</td></tr>
<tr><td>Kecamatan</td><td>${d.kecamatan || '-'}</td></tr>
<tr><td>Kabupaten</td><td>${d.kabupaten || '-'}</td></tr>
<tr><td>Provinsi</td><td>${d.provinsi || '-'}</td></tr>
<tr><td>Agama</td><td>${d.agama || '-'}</td></tr>
<tr><td>Status</td><td>${d.status || '-'}</td></tr>
<tr><td>Pekerjaan</td><td>${d.pekerjaan || '-'}</td></tr>
<tr><td>Warga Negara</td><td>${d.kewarganegaraan || '-'}</td></tr>
</table>
<p class="mb-0 text-muted">Gunakan data ini untuk mengisi formulir?</p>
</div>
`,
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Ya, Isi Formulir',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.value) { // SweetAlert 2 uses result.value or result.isConfirmed
// Auto-fill logic
if(d.nik) document.getElementsByName('nik')[0].value = d.nik;
if(d.nama) document.getElementsByName('nama')[0].value = d.nama;
if(d.tempat_lh) document.getElementsByName('tempat_lh')[0].value = d.tempat_lh;
if(d.tgl_lh) document.getElementsByName('tgl_lh')[0].value = d.tgl_lh;
if(d.je_kel) {
var jkSelect = document.getElementsByName('jekel')[0];
// Direct match first (LK/PR)
for (var i = 0; i < jkSelect.options.length; i++) {
// Check value (LK/PR) or Text
if (jkSelect.options[i].value === d.je_kel || jkSelect.options[i].text === d.je_kel) {
jkSelect.selectedIndex = i;
break;
}
}
}
if(d.desa) document.getElementsByName('desa')[0].value = d.desa;
if(d.kecamatan) document.getElementsByName('kecamatan')[0].value = d.kecamatan;
if(d.kabupaten) document.getElementsByName('kabupaten')[0].value = d.kabupaten;
if(d.provinsi) document.getElementsByName('provinsi')[0].value = d.provinsi;
if(d.provinsi) document.getElementsByName('provinsi')[0].value = d.provinsi;
document.getElementsByName('rt')[0].value = d.rt || '000';
document.getElementsByName('rw')[0].value = d.rw || '000';
if(d.agama) document.getElementsByName('agama')[0].value = d.agama;
if(d.kewarganegaraan) document.getElementsByName('kewarganegaraan')[0].value = d.kewarganegaraan;
if(d.status) {
var kawinSelect = document.getElementsByName('kawin')[0];
for (var i = 0; i < kawinSelect.options.length; i++) {
if (kawinSelect.options[i].text === d.status || kawinSelect.options[i].text.includes(d.status)) {
kawinSelect.selectedIndex = i;
break;
}
}
}
if(d.pekerjaan) document.getElementsByName('pekerjaan')[0].value = d.pekerjaan;
Swal.fire('Berhasil!', 'Formulir telah diisi otomatis.', 'success');
}
});
} else {
Swal.fire('Gagal Scan', data.message, 'error');
}
})
.catch(err => {
console.error(err);
Swal.fire('Error', 'Terjadi kesalahan: ' + err.message, 'error');
})
.finally(() => {
btn.innerHTML = originalText;
btn.disabled = false;
});
});
});
</script>
<?php
if (isset ($_POST['Simpan'])){
$sumber = @$_FILES['foto_ktp']['tmp_name'];
$target = 'foto/ktp/';
$nama_file = @$_FILES['foto_ktp']['name'];
$pindah = move_uploaded_file($sumber, $target.$nama_file);
// Cek apakah ada foto hasil crop?
if (!empty($_POST['foto_cropped'])) {
$data = $_POST['foto_cropped'];
// Hapus header data URL (data:image/jpeg;base64,...)
$parts = explode(',', $data);
$data = $parts[1];
$data = base64_decode($data);
// Buat nama file baru
$nama_file = "KTP-" . time() . ".jpg";
file_put_contents($target . $nama_file, $data);
} else {
// Upload normal jika tidak crop
$sumber = @$_FILES['foto_ktp']['tmp_name'];
if(!empty($sumber)) {
move_uploaded_file($sumber, $target.$nama_file);
}
}
//mulai proses simpan data
$sql_simpan = "INSERT INTO tb_pdd (nik, nama, tempat_lh, tgl_lh, jekel, desa, rt, rw, agama, kawin, pekerjaan, foto_ktp, status) VALUES (
'".$_POST['nik']."',
'".$_POST['nama']."',
'".$_POST['tempat_lh']."',
'".$_POST['tgl_lh']."',
'".$_POST['jekel']."',
'".$_POST['desa']."',
'".$_POST['rt']."',
'".$_POST['rw']."',
'".$_POST['agama']."',
'".$_POST['kawin']."',
'".$_POST['pekerjaan']."',
'".$nama_file."',
'Ada')";
$nik = $_POST['nik'];
$cek_nik = mysqli_query($koneksi, "SELECT * FROM tb_pdd WHERE nik='$nik'");
if(mysqli_num_rows($cek_nik) > 0){
echo "<script>
Swal.fire({title: 'Gagal',text: 'NIK sudah terdaftar dalam sistem!',icon: 'error',confirmButtonText: 'OK'
}).then((result) => {if (result.value){
window.location = 'index.php?page=add-pend';
}
})</script>";
return;
}
// Sanitize Input to prevent SQL Injection & Syntax Errors
$nik = mysqli_real_escape_string($koneksi, $_POST['nik']);
$nama = mysqli_real_escape_string($koneksi, $_POST['nama']);
$tempat_lh = mysqli_real_escape_string($koneksi, $_POST['tempat_lh']);
$tgl_lh = mysqli_real_escape_string($koneksi, $_POST['tgl_lh']);
$jekel = mysqli_real_escape_string($koneksi, $_POST['jekel']);
$desa = mysqli_real_escape_string($koneksi, $_POST['desa']);
$rt = mysqli_real_escape_string($koneksi, $_POST['rt']);
$rw = mysqli_real_escape_string($koneksi, $_POST['rw']);
$agama = mysqli_real_escape_string($koneksi, $_POST['agama']);
$kawin = mysqli_real_escape_string($koneksi, $_POST['kawin']);
$pekerjaan = mysqli_real_escape_string($koneksi, $_POST['pekerjaan']);
$kecamatan = mysqli_real_escape_string($koneksi, $_POST['kecamatan']);
$kabupaten = mysqli_real_escape_string($koneksi, $_POST['kabupaten']);
$provinsi = mysqli_real_escape_string($koneksi, $_POST['provinsi']);
$kewarganegaraan = mysqli_real_escape_string($koneksi, $_POST['kewarganegaraan']);
$sql_simpan = "INSERT INTO tb_pdd (nik, nama, tempat_lh, tgl_lh, jekel, desa, rt, rw, agama, kawin, pekerjaan, foto_ktp, status, kecamatan, kabupaten, provinsi, kewarganegaraan) VALUES (
'$nik',
'$nama',
'$tempat_lh',
'$tgl_lh',
'$jekel',
'$desa',
'$rt',
'$rw',
'$agama',
'$kawin',
'$pekerjaan',
'$nama_file',
'Ada',
'$kecamatan',
'$kabupaten',
'$provinsi',
'$kewarganegaraan')";
$query_simpan = mysqli_query($koneksi, $sql_simpan);
mysqli_close($koneksi);
@@ -136,7 +454,7 @@
})</script>";
}else{
echo "<script>
Swal.fire({title: 'Tambah Data Gagal',text: '',icon: 'error',confirmButtonText: 'OK'
Swal.fire({title: 'Tambah Data Gagal',text: '<?php echo mysqli_error($koneksi); ?>',icon: 'error',confirmButtonText: 'OK'
}).then((result) => {if (result.value){
window.location = 'index.php?page=add-pend';
}

View File

@@ -73,10 +73,9 @@
class="btn btn-success btn-sm">
<i class="fa fa-edit"></i>
</a>
<a href="?page=del-pend&kode=<?php echo $data['id_pend']; ?>" onclick="return confirm('Apakah anda yakin hapus data ini ?')"
title="Hapus" class="btn btn-danger btn-sm">
<a href="#" onclick="HapusData('<?php echo $data['id_pend']; ?>')" title="Hapus" class="btn btn-danger btn-sm">
<i class="fa fa-trash"></i>
</>
</a>
</td>
</tr>
@@ -88,4 +87,24 @@
</table>
</div>
</div>
<!-- /.card-body -->
</div>
<!-- /.card-body -->
<script>
function HapusData(id){
Swal.fire({
title: 'Apakah anda yakin?',
text: "Data yang dihapus tidak dapat dikembalikan!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Ya, Hapus!',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.value) {
window.location.href = "?page=del-pend&kode=" + id;
}
})
}
</script>

View File

@@ -23,11 +23,54 @@
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Foto KTP</label>
<div class="col-sm-6">
<?php if(!empty($data_cek['foto_ktp'])): ?>
<img id="preview_ktp" src="foto/ktp/<?php echo $data_cek['foto_ktp']; ?>" width="200px" style="border: 2px solid #6c757d; padding: 3px; border-radius: 5px; cursor: pointer; display:block;" class="mb-2">
<?php else: ?>
<img id="preview_ktp" src="dist/img/noimage.png" width="200px" style="border: 2px solid #6c757d; padding: 3px; border-radius: 5px; display:block;" class="mb-2">
<?php endif; ?>
<input type="file" class="form-control mb-2" id="foto_ktp" name="foto_ktp" accept=".jpg, .jpeg, .png">
<button type="button" class="btn btn-info btn-block" id="btnScanKTP">
<i class="fas fa-magic"></i> Scan Ulang dengan AI
</button>
<small class="text-muted">Pilih foto, crop, lalu scan.</small>
<input type="hidden" id="foto_cropped" name="foto_cropped">
</div>
</div>
<!-- Modal Crop -->
<div class="modal fade" id="modalCrop" tabindex="-1" role="dialog" aria-labelledby="modalCropLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalCropLabel">Potong & Putar Foto</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="img-container" style="max-height: 500px;">
<img id="image-to-crop" src="" style="max-width: 100%; display: block;">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" id="btnRotateLeft" title="Putar Kiri"><i class="fas fa-undo"></i></button>
<button type="button" class="btn btn-secondary" id="btnRotateRight" title="Putar Kanan"><i class="fas fa-redo"></i></button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
<button type="button" class="btn btn-primary" id="btnCrop">Potong & Simpan</button>
</div>
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">NIK</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="nik" name="nik" value="<?php echo $data_cek['nik']; ?>"
/>
required>
</div>
</div>
@@ -40,7 +83,7 @@
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">TTL</label>
<label class="col-sm-2 col-form-label">Tempat/Tanggal Lahir</label>
<div class="col-sm-3">
<input type="text" class="form-control" id="tempat_lh" name="tempat_lh" value="<?php echo $data_cek['tempat_lh']; ?>"
/>
@@ -58,11 +101,12 @@
<option value="">-- Pilih jekel --</option>
<?php
//menhecek data yg dipilih sebelumnya
if ($data_cek['jekel'] == "LK") echo "<option value='LK' selected>LK</option>";
else echo "<option value='LK'>LK</option>";
//menhecek data yg dipilih sebelumnya
if ($data_cek['jekel'] == "LK") echo "<option value='LK' selected>LAKI-LAKI</option>";
else echo "<option value='LK'>LAKI-LAKI</option>";
if ($data_cek['jekel'] == "PR") echo "<option value='PR' selected>PR</option>";
else echo "<option value='PR'>PR</option>";
if ($data_cek['jekel'] == "PR") echo "<option value='PR' selected>PEREMPUAN</option>";
else echo "<option value='PR'>PEREMPUAN</option>";
?>
</select>
</div>
@@ -72,7 +116,31 @@
<label class="col-sm-2 col-form-label">Desa</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="desa" name="desa" value="<?php echo $data_cek['desa']; ?>"
/>
required>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Kecamatan</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="kecamatan" name="kecamatan" value="<?php echo $data_cek['kecamatan']; ?>"
required>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Kabupaten</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="kabupaten" name="kabupaten" value="<?php echo $data_cek['kabupaten']; ?>"
required>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Provinsi</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="provinsi" name="provinsi" value="<?php echo $data_cek['provinsi']; ?>"
required>
</div>
</div>
@@ -123,26 +191,19 @@
<label class="col-sm-2 col-form-label">Pekerjaan</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="pekerjaan" name="pekerjaan" value="<?php echo $data_cek['pekerjaan']; ?>"
/>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Foto KTP</label>
<div class="col-sm-6">
<?php if(!empty($data_cek['foto_ktp'])): ?>
<img src="foto/ktp/<?php echo $data_cek['foto_ktp']; ?>" width="200px" style="border: 2px solid #6c757d; padding: 3px; border-radius: 5px; cursor: pointer;" onclick="showImage(this.src)" title="Klik untuk memperbesar">
<?php else: ?>
<img src="dist/img/noprofile.png" width="200px" style="border: 2px solid #6c757d; padding: 3px; border-radius: 5px;">
<?php endif; ?>
required>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Ubah Foto</label>
<label class="col-sm-2 col-form-label">Kewarganegaraan</label>
<div class="col-sm-6">
<input type="file" class="form-control" id="foto_ktp" name="foto_ktp">
<input type="text" class="form-control" id="kewarganegaraan" name="kewarganegaraan" value="<?php echo $data_cek['kewarganegaraan']; ?>"
required>
</div>
</div>
</div>
<div class="card-footer">
<input type="submit" name="Ubah" value="Simpan" class="btn btn-success">
@@ -151,50 +212,286 @@
</form>
</div>
<script>
// Preview Image
window.addEventListener('load', function() {
// Cropper Logic
var cropper;
var image = document.getElementById('image-to-crop');
var inputImage = document.getElementById('foto_ktp');
var modal = $('#modalCrop');
inputImage.addEventListener('change', function(e) {
var files = e.target.files;
var done = function(url) {
inputImage.value = ''; // Clear
image.src = url;
modal.modal('show');
};
var reader;
var file;
var url;
if (files && files.length > 0) {
file = files[0];
if (URL) {
done(URL.createObjectURL(file));
} else if (FileReader) {
reader = new FileReader();
reader.onload = function(e) {
done(reader.result);
};
reader.readAsDataURL(file);
}
}
});
modal.on('shown.bs.modal', function() {
cropper = new Cropper(image, {
aspectRatio: 1.58,
viewMode: 1,
autoCropArea: 1,
});
}).on('hidden.bs.modal', function() {
cropper.destroy();
cropper = null;
});
document.getElementById('btnRotateLeft').addEventListener('click', function() {
cropper.rotate(-90);
});
document.getElementById('btnRotateRight').addEventListener('click', function() {
cropper.rotate(90);
});
document.getElementById('btnCrop').addEventListener('click', function() {
var canvas;
if (cropper) {
canvas = cropper.getCroppedCanvas({ width: 1000 });
var base64 = canvas.toDataURL('image/jpeg');
var output = document.getElementById('preview_ktp');
output.src = base64;
document.getElementById('foto_cropped').value = base64;
modal.modal('hide');
}
});
// Scan
document.getElementById('btnScanKTP').addEventListener('click', function() {
var fileInput = document.getElementById('foto_ktp');
var croppedVal = document.getElementById('foto_cropped').value;
if(fileInput.files.length === 0 && !croppedVal) {
Swal.fire('Info', 'Silakan pilih foto KTP baru terlebih dahulu.', 'warning');
return;
}
var btn = this;
var originalText = btn.innerHTML;
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Menganalisa...';
btn.disabled = true;
var formData = new FormData();
var croppedData = document.getElementById('foto_cropped').value;
if (croppedData) {
// Convert Base64 to Blob
var byteString = atob(croppedData.split(',')[1]);
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
var blob = new Blob([ab], { type: 'image/jpeg' });
formData.append('image', blob, 'cropped_ktp_edit.jpg');
} else if (fileInput.files.length > 0) {
formData.append('image', fileInput.files[0]);
} else {
// Handle case where specific file input is empty but user wants to rescan existing?
// Currently flow requires NEW photo for scan.
Swal.fire('Info', 'Belum ada foto baru yang dipilih / dicrop.', 'warning');
btn.disabled = false;
btn.innerHTML = originalText;
return;
}
formData.append('type', 'ktp');
fetch('admin/api/ocr_helper.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if(data.success) {
var d = data.data;
// Show Confirmation
Swal.fire({
title: 'Hasil Scan AI',
html: `
<div style="text-align: left; font-size: 0.9rem;">
<table class="table table-bordered table-sm">
<tr><td width="30%">NIK</td><td><b>${d.nik || '-'}</b></td></tr>
<tr><td>Nama</td><td><b>${d.nama || '-'}</b></td></tr>
<tr><td>TTL</td><td>${d.tempat_lh || '-'}, ${d.tgl_lh || '-'}</td></tr>
<tr><td>JK</td><td>${d.je_kel || '-'}</td></tr>
<tr><td>RT/RW</td><td>${d.rt || '000'}/${d.rw || '000'}</td></tr>
<tr><td>Desa</td><td>${d.desa || '-'}</td></tr>
<tr><td>Kecamatan</td><td>${d.kecamatan || '-'}</td></tr>
<tr><td>Kabupaten</td><td>${d.kabupaten || '-'}</td></tr>
<tr><td>Provinsi</td><td>${d.provinsi || '-'}</td></tr>
<tr><td>Warga Negara</td><td>${d.kewarganegaraan || '-'}</td></tr>
</table>
<p class="mb-0 text-muted">Perbarui data formulir?</p>
</div>
`,
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Ya, Update',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.value) {
// Auto-fill logic
if(d.nik) document.getElementsByName('nik')[0].value = d.nik;
if(d.nama) document.getElementsByName('nama')[0].value = d.nama;
if(d.tempat_lh) document.getElementsByName('tempat_lh')[0].value = d.tempat_lh;
if(d.tgl_lh) document.getElementsByName('tgl_lh')[0].value = d.tgl_lh;
if(d.je_kel) {
var jkSelect = document.getElementsByName('jekel')[0];
for (var i = 0; i < jkSelect.options.length; i++) {
// Check value (LK/PR) or Text
if (jkSelect.options[i].value === d.je_kel || jkSelect.options[i].text === d.je_kel) {
jkSelect.selectedIndex = i;
break;
}
}
}
if(d.desa) document.getElementsByName('desa')[0].value = d.desa;
if(d.kecamatan) document.getElementsByName('kecamatan')[0].value = d.kecamatan;
if(d.kabupaten) document.getElementsByName('kabupaten')[0].value = d.kabupaten;
if(d.provinsi) document.getElementsByName('provinsi')[0].value = d.provinsi;
document.getElementsByName('rt')[0].value = d.rt || '000';
document.getElementsByName('rw')[0].value = d.rw || '000';
if(d.agama) document.getElementsByName('agama')[0].value = d.agama;
if(d.kewarganegaraan) document.getElementsByName('kewarganegaraan')[0].value = d.kewarganegaraan;
if(d.status) {
var kawinSelect = document.getElementsByName('kawin')[0];
for (var i = 0; i < kawinSelect.options.length; i++) {
if (kawinSelect.options[i].text === d.status || kawinSelect.options[i].text.includes(d.status)) {
kawinSelect.selectedIndex = i;
break;
}
}
}
if(d.pekerjaan) document.getElementsByName('pekerjaan')[0].value = d.pekerjaan;
Swal.fire('Berhasil!', 'Data formulir telah diperbarui.', 'success');
}
});
} else {
Swal.fire('Gagal', data.message, 'error');
}
})
.catch(err => {
console.error(err);
Swal.fire('Error', 'Terjadi kesalahan: ' + err.message, 'error');
})
.finally(() => {
btn.innerHTML = originalText;
btn.disabled = false;
});
});
});
</script>
<?php
if (isset ($_POST['Ubah'])){
$sumber = @$_FILES['foto_ktp']['tmp_name'];
$target = 'foto/ktp/';
$nama_file = @$_FILES['foto_ktp']['name'];
$pindah = move_uploaded_file($sumber, $target.$nama_file);
$sumber = @$_FILES['foto_ktp']['tmp_name'];
$has_new_photo = false;
if(!empty($sumber)){
// Cek Crop
if (!empty($_POST['foto_cropped'])) {
$data = $_POST['foto_cropped'];
$parts = explode(',', $data);
$data = $parts[1];
$data = base64_decode($data);
$nama_file = "KTP-" . time() . ".jpg";
file_put_contents($target . $nama_file, $data);
$has_new_photo = true;
} elseif (!empty($sumber)) {
// Normal Upload
move_uploaded_file($sumber, $target.$nama_file);
$has_new_photo = true;
}
// Sanitize Input
$id_pend = mysqli_real_escape_string($koneksi, $_POST['id_pend']);
$nik = mysqli_real_escape_string($koneksi, $_POST['nik']);
$nama = mysqli_real_escape_string($koneksi, $_POST['nama']);
$tempat_lh = mysqli_real_escape_string($koneksi, $_POST['tempat_lh']);
$tgl_lh = mysqli_real_escape_string($koneksi, $_POST['tgl_lh']);
$jekel = mysqli_real_escape_string($koneksi, $_POST['jekel']);
$desa = mysqli_real_escape_string($koneksi, $_POST['desa']);
$rt = mysqli_real_escape_string($koneksi, $_POST['rt']);
$rw = mysqli_real_escape_string($koneksi, $_POST['rw']);
$agama = mysqli_real_escape_string($koneksi, $_POST['agama']);
$kawin = mysqli_real_escape_string($koneksi, $_POST['kawin']);
$pekerjaan = mysqli_real_escape_string($koneksi, $_POST['pekerjaan']);
$kecamatan = mysqli_real_escape_string($koneksi, $_POST['kecamatan']);
$kabupaten = mysqli_real_escape_string($koneksi, $_POST['kabupaten']);
$provinsi = mysqli_real_escape_string($koneksi, $_POST['provinsi']);
$kewarganegaraan = mysqli_real_escape_string($koneksi, $_POST['kewarganegaraan']);
if($has_new_photo){
$foto= $data_cek['foto_ktp'];
if (file_exists("foto/ktp/$foto")){
unlink("foto/ktp/$foto");}
$sql_ubah = "UPDATE tb_pdd SET
nik='".$_POST['nik']."',
nama='".$_POST['nama']."',
tempat_lh='".$_POST['tempat_lh']."',
tgl_lh='".$_POST['tgl_lh']."',
jekel='".$_POST['jekel']."',
desa='".$_POST['desa']."',
rt='".$_POST['rt']."',
rw='".$_POST['rw']."',
agama='".$_POST['agama']."',
kawin='".$_POST['kawin']."',
pekerjaan='".$_POST['pekerjaan']."',
foto_ktp='".$nama_file."'
WHERE id_pend='".$_POST['id_pend']."'";
nik='$nik',
nama='$nama',
tempat_lh='$tempat_lh',
tgl_lh='$tgl_lh',
jekel='$jekel',
desa='$desa',
rt='$rt',
rw='$rw',
agama='$agama',
kawin='$kawin',
pekerjaan='$pekerjaan',
kecamatan='$kecamatan',
kabupaten='$kabupaten',
provinsi='$provinsi',
kewarganegaraan='$kewarganegaraan',
foto_ktp='$nama_file'
WHERE id_pend='$id_pend'";
$query_ubah = mysqli_query($koneksi, $sql_ubah);
}elseif(empty($sumber)){
}else{
$sql_ubah = "UPDATE tb_pdd SET
nik='".$_POST['nik']."',
nama='".$_POST['nama']."',
tempat_lh='".$_POST['tempat_lh']."',
tgl_lh='".$_POST['tgl_lh']."',
jekel='".$_POST['jekel']."',
desa='".$_POST['desa']."',
rt='".$_POST['rt']."',
rw='".$_POST['rw']."',
agama='".$_POST['agama']."',
kawin='".$_POST['kawin']."',
pekerjaan='".$_POST['pekerjaan']."'
WHERE id_pend='".$_POST['id_pend']."'";
nik='$nik',
nama='$nama',
tempat_lh='$tempat_lh',
tgl_lh='$tgl_lh',
jekel='$jekel',
desa='$desa',
rt='$rt',
rw='$rw',
agama='$agama',
kawin='$kawin',
pekerjaan='$pekerjaan',
kecamatan='$kecamatan',
kabupaten='$kabupaten',
provinsi='$provinsi',
kewarganegaraan='$kewarganegaraan'
WHERE id_pend='$id_pend'";
$query_ubah = mysqli_query($koneksi, $sql_ubah);
}
mysqli_close($koneksi);

View File

@@ -48,7 +48,7 @@
</tr>
<tr>
<td style="width: 150px">
<b>TTL</b>
<b>Tempat/Tanggal Lahir</b>
</td>
<td>:
<?php echo $data_cek['tempat_lh']; ?>
@@ -61,7 +61,11 @@
<b>Jenis Kelamin</b>
</td>
<td>:
<?php echo $data_cek['jekel']; ?>
<?php
if($data_cek['jekel'] == 'LK') echo "LAKI-LAKI";
elseif($data_cek['jekel'] == 'PR') echo "PEREMPUAN";
else echo $data_cek['jekel'];
?>
</td>
</tr>
<tr>
@@ -74,6 +78,30 @@
<?php echo $data_cek['rw']; ?>
</td>
</tr>
<tr>
<td style="width: 150px">
<b>Kecamatan</b>
</td>
<td>:
<?php echo $data_cek['kecamatan']; ?>
</td>
</tr>
<tr>
<td style="width: 150px">
<b>Kabupaten</b>
</td>
<td>:
<?php echo $data_cek['kabupaten']; ?>
</td>
</tr>
<tr>
<td style="width: 150px">
<b>Provinsi</b>
</td>
<td>:
<?php echo $data_cek['provinsi']; ?>
</td>
</tr>
<tr>
<td style="width: 150px">
<b>Agama</b>
@@ -97,6 +125,15 @@
<td>:
<?php echo $data_cek['pekerjaan']; ?>
</td>
</tr>
<tr>
<td style="width: 150px">
<b>Kewarganegaraan</b>
</td>
<td>:
<?php echo $data_cek['kewarganegaraan']; ?>
</td>
</tr>

22
fix_kk_db.php Normal file
View File

@@ -0,0 +1,22 @@
<?php
include "inc/koneksi.php";
$queries = [
"ALTER TABLE tb_kk MODIFY desa VARCHAR(100)",
"ALTER TABLE tb_kk MODIFY kec VARCHAR(100)",
"ALTER TABLE tb_kk MODIFY kab VARCHAR(100)",
"ALTER TABLE tb_kk MODIFY prov VARCHAR(100)",
"ALTER TABLE tb_kk MODIFY kepala VARCHAR(100)",
"ALTER TABLE tb_kk MODIFY no_kk VARCHAR(50)"
];
foreach ($queries as $sql) {
if ($koneksi->query($sql) === TRUE) {
echo "Success: $sql<br>";
} else {
echo "Error: " . $koneksi->error . "<br>";
}
}
$koneksi->close();
?>

View File

@@ -37,6 +37,8 @@
<!-- Select2 -->
<link rel="stylesheet" href="plugins/select2/css/select2.min.css">
<link rel="stylesheet" href="plugins/select2-bootstrap4-theme/select2-bootstrap4.min.css">
<!-- Cropper.js -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.min.css" rel="stylesheet">
<!-- Google Font: Source Sans Pro -->
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700" rel="stylesheet">
<!-- Alert -->
@@ -701,6 +703,8 @@
<script src="plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- Select2 -->
<script src="plugins/select2/js/select2.full.min.js"></script>
<!-- Cropper.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.min.js"></script>
<!-- DataTables -->
<script src="plugins/datatables/jquery.dataTables.js"></script>
<script src="plugins/datatables-bs4/js/dataTables.bootstrap4.js"></script>