Compare commits

2 Commits
master ... main

371 changed files with 1494 additions and 6265 deletions

View File

@@ -1,18 +0,0 @@
# Docker ignore file
.git
.gitignore
node_modules
npm-debug.log
yarn-debug.log
yarn-error.log
.DS_Store
Thumbs.db
.vscode
.idea
*.log
php_server.pid
php_server.log
.env
docker-compose.override.yml
*.md
README*

View File

@@ -1,9 +0,0 @@
# Google Gemini API Configuration
# Get your API key from: https://makersuite.google.com/app/apikey
GEMINI_API_KEY=your_gemini_api_key_here
# Database Configuration (if needed in future)
# DB_HOST=localhost
# DB_NAME=sidak_db
# DB_USER=root
# DB_PASSWORD=

8
.gitignore vendored Executable file → Normal file
View File

@@ -13,12 +13,10 @@ Thumbs.db
# Dependencies (if any in future) # Dependencies (if any in future)
node_modules/ node_modules/
vendor/ vendor/
!plugins/vendor/
# Uploads # Uploads
foto/ foto/
# API Keys and Environment Variables # Debug files
admin/api/config_api.php debug_ai.txt
.env debug_log.txt
.env.*.local

View File

@@ -1,38 +0,0 @@
FROM php:8.2-apache
# Install necessary PHP extensions and tools
RUN apt-get update && apt-get install -y \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
libzip-dev \
zip \
unzip \
curl \
git \
mariadb-client \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j$(nproc) gd \
&& docker-php-ext-install mysqli pdo pdo_mysql zip \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Enable Apache rewrite module
RUN a2enmod rewrite
# Set working directory
WORKDIR /var/www/html
# Copy application files
COPY . /var/www/html/
# Set proper permissions
RUN chown -R www-data:www-data /var/www/html \
&& chmod -R 755 /var/www/html
# Update koneksi.php to use environment variables
RUN sed -i 's/\$koneksi = new mysqli ("localhost","sidak_user","sidak_pass","data_penduduk");/\$db_host = getenv("DB_HOST") ?: "localhost";\n\$db_user = getenv("DB_USER") ?: "sidak_user";\n\$db_pass = getenv("DB_PASS") ?: "sidak_pass";\n\$db_name = getenv("DB_NAME") ?: "data_penduduk";\n\$koneksi = new mysqli (\$db_host,\$db_user,\$db_pass,\$db_name);/g' inc/koneksi.php
EXPOSE 80
CMD ["apache2-foreground"]

View File

@@ -1,115 +0,0 @@
# 🔐 Keamanan API Key SIDAK
## **⚠️ PENTING: API Key Terdeteksi di Repository Git**
File `admin/api/config_api.php` sebelumnya mengandung kunci API Gemini yang terekspos di repository Git. Kunci ini telah diamankan dengan sistem environment variables.
## **🛡️ Langkah-Langkah Pengamanan yang Telah Dilakukan:**
1. **API Key dihapus dari Git tracking:**
```bash
git rm --cached admin/api/config_api.php
```
2. **File konfigurasi dimodifikasi untuk menggunakan environment variables:**
- `admin/api/config_api.php` sekarang membaca dari file `.env`
- Gunakan template `.env.example` untuk membuat `.env`
3. **File sensitif ditambahkan ke `.gitignore`:**
```
admin/api/config_api.php
.env
.env.*.local
```
## **🚀 Deployment Instructions:**
### **1. Untuk Development Lokal:**
```bash
# Salin template .env
cp .env.example .env
# Edit .env dengan API key Anda
nano .env # atau editor favorit Anda
```
### **2. Untuk Production Server:**
```bash
# Buat file .env di server
cat > /var/www/sidak/.env << 'EOF'
# Google Gemini API Configuration
GEMINI_API_KEY=your_actual_production_key_here
EOF
# Pastikan permission aman
chmod 600 /var/www/sidak/.env
chown www-data:www-data /var/www/sidak/.env
```
### **3. Get New API Key (jika perlu):**
1. Kunjungi [Google AI Studio](https://makersuite.google.com/app/apikey)
2. Login dengan akun Google
3. Create API Key → Copy key baru
4. Update file `.env` di server
## **📁 Struktur File yang Aman:**
```
sidak/
├── .env # ⚠️ JANGAN commit (sudah di .gitignore)
├── .env.example # ✅ Template aman untuk commit
├── .gitignore # ✅ Menyertakan .env dan config_api.php
├── admin/
│ └── api/
│ ├── config_api.php # ✅ Membaca dari environment variables
│ └── ocr_helper.php # ✅ Menggunakan GEMINI_API_KEY dari config
└── README_API_SECURITY.md # ✅ Dokumentasi ini
```
## **🔧 Testing Configuration:**
Untuk memastikan konfigurasi bekerja:
```php
<?php
// Test script: test_api_config.php
require_once 'admin/api/config_api.php';
echo "GEMINI_API_KEY: " . (defined('GEMINI_API_KEY') ? '✅ Set' : '❌ Not set') . "\n";
echo "GEMINI_API_URL: " . GEMINI_API_URL . "\n";
if (empty(GEMINI_API_KEY)) {
echo "❌ ERROR: API key tidak terdeteksi.\n";
echo "Pastikan file .env ada dan berisi GEMINI_API_KEY=your_key\n";
} else {
echo "✅ Konfigurasi API siap digunakan.\n";
}
?>
```
## **🔄 Jika Terjadi Masalah:**
### **Masalah: "API key not set"**
**Solusi:**
1. Pastikan file `.env` ada di root directory
2. Pastikan permission file `.env` dapat dibaca oleh PHP
3. Restart web server jika perlu: `sudo service apache2 restart`
### **Masalah: "403 Forbidden" dari Gemini API**
**Solusi:**
1. Periksa apakah API key valid di [Google AI Studio](https://makersuite.google.com/app/apikey)
2. Pastikan billing enabled di Google Cloud Console
3. Cek quota usage di Google Cloud Console
## **📞 Support:**
Jika menemukan masalah keamanan:
1. **Segera putar API key** di Google AI Studio
2. Update file `.env` di semua environment
3. Hubungi developer: [wartana@example.com]
---
**⚠️ REMINDER:** JANGAN pernah commit file `.env` atau `admin/api/config_api.php` ke repository Git. Selalu gunakan `.env.example` sebagai template.
**Terakhir diperbarui:** 22 Januari 2026

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

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');
?>

77
admin/api/ocr_helper.php Executable file → Normal file
View File

@@ -1,7 +1,6 @@
<?php <?php
header('Content-Type: application/json'); header('Content-Type: application/json');
require_once 'config_api.php'; require_once 'config_api.php';
set_time_limit(300);
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
echo json_encode(['success' => false, 'message' => 'Invalid request method']); echo json_encode(['success' => false, 'message' => 'Invalid request method']);
@@ -20,27 +19,31 @@ $mimeType = mime_content_type($imagePath);
// Construct Prompt based on Type // Construct Prompt based on Type
if ($type === 'kk') { 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."; $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': '...', 'tempat_lh': '...', 'tgl_lh': 'YYYY-MM-DD', 'jekel': 'LK/PR', 'agama': '...', 'kawin': '...', 'pekerjaan': '...', 'kewarganegaraan': 'WNI/WNA' } ] (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 { } else {
// Default to KTP // 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."; $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 Ollama App // Payload for Gemini
$data = [ $data = [
"model" => "qwen2.5vl:3b", "contents" => [
"messages" => [
[ [
"role" => "user", "parts" => [
"content" => $promptText, ["text" => $promptText],
"images" => [$imageData] [
"inline_data" => [
"mime_type" => $mimeType,
"data" => $imageData
]
]
]
] ]
], ]
"stream" => false
]; ];
// Send Request // Send Request
$ch = curl_init(OLLAMA_API_URL); $ch = curl_init(GEMINI_API_URL . '?key=' . GEMINI_API_KEY);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
@@ -64,13 +67,14 @@ if (isset($result['error'])) {
exit; exit;
} }
if (!isset($result['message']['content'])) { if (!isset($result['candidates'][0]['content']['parts'][0]['text'])) {
echo json_encode(['success' => false, 'message' => 'No text returned from AI', 'raw' => $result]); echo json_encode(['success' => false, 'message' => 'No text returned from AI', 'raw' => $result]);
exit; exit;
} }
// Parse AI Response // Parse AI Response
$rawText = $result['message']['content']; $rawText = $result['candidates'][0]['content']['parts'][0]['text'];
file_put_contents('../../debug_ai.txt', "AI Response:\n" . $rawText . "\n-------------------\n", FILE_APPEND);
// Remove Markdown Code Blocks if any (```json ... ```) // Remove Markdown Code Blocks if any (```json ... ```)
$cleanJson = preg_replace('/^```json\s*|\s*```$/', '', trim($rawText)); $cleanJson = preg_replace('/^```json\s*|\s*```$/', '', trim($rawText));
@@ -88,52 +92,5 @@ if (!$parsedData) {
exit; exit;
} }
if ($type === 'ktp') {
// 1. Parse Status Perkawinan
if (!empty($parsedData['status'])) {
$st = strtoupper($parsedData['status']);
if (strpos($st, 'BELUM') !== false) {
$parsedData['status'] = 'Belum';
} elseif (strpos($st, 'CERAI MATI') !== false) {
$parsedData['status'] = 'Cerai Mati';
} elseif (strpos($st, 'CERAI HIDUP') !== false) {
$parsedData['status'] = 'Cerai Hidup';
} elseif (strpos($st, 'KAWIN') !== false) {
$parsedData['status'] = 'Sudah';
}
}
// 2. Parse Jenis Kelamin
if (!empty($parsedData['je_kel'])) {
$jk = strtoupper($parsedData['je_kel']);
if (strpos($jk, 'LAKI') !== false || $jk === 'L') {
$parsedData['je_kel'] = 'LK';
} elseif (strpos($jk, 'PEREMPUAN') !== false || $jk === 'P') {
$parsedData['je_kel'] = 'PR';
}
}
// 3. Parse Tempat & Tanggal Lahir (KTP format: "TEMPAT, DD-MM-YYYY")
if (!empty($parsedData['tempat_lh'])) {
$ttl = $parsedData['tempat_lh'];
if (strpos($ttl, ',') !== false) {
[$tempat, $tgl] = explode(',', $ttl, 2);
$parsedData['tempat_lh'] = trim($tempat);
if (empty($parsedData['tgl_lh'])) {
$parsedData['tgl_lh'] = trim($tgl);
}
}
}
// 4. Format Tanggal Lahir to YYYY-MM-DD for HTML input[type=date]
if (!empty($parsedData['tgl_lh'])) {
$tgl_lh = trim($parsedData['tgl_lh']);
// If it looks like DD-MM-YYYY or DD/MM/YYYY
if (preg_match('/^(\d{2})[\/\-](\d{2})[\/\-](\d{4})$/', $tgl_lh, $matches)) {
$parsedData['tgl_lh'] = $matches[3] . '-' . $matches[2] . '-' . $matches[1];
}
}
}
echo json_encode(['success' => true, 'data' => $parsedData]); echo json_encode(['success' => true, 'data' => $parsedData]);
?> ?>

166
admin/datang/add_datang.php Executable file → Normal file
View File

@@ -1,6 +1,3 @@
<?php
$selected_id = isset($_GET['selected_id']) ? (int)$_GET['selected_id'] : 0;
?>
<div class="card card-primary"> <div class="card card-primary">
<div class="card-header"> <div class="card-header">
<h3 class="card-title"> <h3 class="card-title">
@@ -9,40 +6,37 @@ $selected_id = isset($_GET['selected_id']) ? (int)$_GET['selected_id'] : 0;
<form action="" method="post" enctype="multipart/form-data"> <form action="" method="post" enctype="multipart/form-data">
<div class="card-body"> <div class="card-body">
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-2 col-form-label">Pendatang</label> <label class="col-sm-2 col-form-label">NIK</label>
<div class="col-sm-6"> <div class="col-sm-6">
<select name="id_pend" id="id_pend" class="form-control select2bs4" required> <input type="text" class="form-control" id="nik" name="nik" placeholder="NIK" required>
<option value="" <?php echo ($selected_id == 0) ? 'selected="selected"' : ''; ?>>- Pilih Penduduk -</option> </div>
<?php </div>
// ambil data dari database
$query = "select * from tb_pdd where status='Ada'"; <div class="form-group row">
$hasil = mysqli_query($koneksi, $query); <label class="col-sm-2 col-form-label">Nama</label>
while ($row = mysqli_fetch_array($hasil)) { <div class="col-sm-6">
?> <input type="text" class="form-control" id="nama_datang" name="nama_datang" placeholder="Nama Pendatang" required>
<option value="<?php echo $row['id_pend'] ?>" <?php echo ($row['id_pend'] == $selected_id) ? 'selected="selected"' : ''; ?>> </div>
<?php echo $row['nik'] ?> </div>
-
<?php echo $row['nama'] ?> <div class="form-group row">
</option> <label class="col-sm-2 col-form-label">Jenis Kelain</label>
<?php <div class="col-sm-3">
} <select name="jekel" id="jekel" class="form-control">
?> <option>- Pilih -</option>
</select> <option>LK</option>
</div> <option>PR</option>
<div class="col-sm-2"> </select>
<a href="?page=add-pend&return_to=add-datang" class="btn btn-outline-primary btn-sm"> </div>
<i class="fa fa-plus"></i> Tambah Penduduk Baru </div>
</a>
</div> <div class="form-group row">
</div> <label class="col-sm-2 col-form-label">Tgl Datang</label>
<div class="col-sm-3">
<div class="form-group row"> <input type="date" class="form-control" id="tgl_datang" name="tgl_datang" required>
<label class="col-sm-2 col-form-label">Tgl Datang</label> </div>
<div class="col-sm-3"> </div>
<input type="date" class="form-control" id="tgl_datang" name="tgl_datang" required>
</div>
</div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-2 col-form-label">Pelapor</label> <label class="col-sm-2 col-form-label">Pelapor</label>
@@ -56,9 +50,9 @@ $selected_id = isset($_GET['selected_id']) ? (int)$_GET['selected_id'] : 0;
while ($row = mysqli_fetch_array($hasil)) { while ($row = mysqli_fetch_array($hasil)) {
?> ?>
<option value="<?php echo $row['id_pend'] ?>"> <option value="<?php echo $row['id_pend'] ?>">
<?php echo htmlspecialchars($row['nik'], ENT_QUOTES); ?> <?php echo $row['nik'] ?>
- -
<?php echo htmlspecialchars($row['nama'], ENT_QUOTES); ?> <?php echo $row['nama'] ?>
</option> </option>
<?php <?php
} }
@@ -77,51 +71,51 @@ $selected_id = isset($_GET['selected_id']) ? (int)$_GET['selected_id'] : 0;
<?php <?php
if (isset ($_POST['Simpan'])){ if (isset ($_POST['Simpan'])){
//mulai proses simpan data //mulai proses simpan data
$sql_simpan = "INSERT INTO tb_datang (nik, nama_datang, jekel, tgl_datang, pelapor) VALUES (
// Sanitize Input to prevent SQL Injection & Syntax Errors '".$_POST['nik']."',
$id_pend = (int)$_POST['id_pend']; // Cast to integer for safety '".$_POST['nama_datang']."',
$tgl_datang = mysqli_real_escape_string($koneksi, trim($_POST['tgl_datang'])); '".$_POST['jekel']."',
$pelapor = (int)$_POST['pelapor']; // Cast to integer for safety '".$_POST['tgl_datang']."',
'".$_POST['pelapor']."')";
// Ambil data penduduk yang dipilih $query_simpan = mysqli_query($koneksi, $sql_simpan);
$sql_pend = "SELECT nik, nama, jekel FROM tb_pdd WHERE id_pend='$id_pend'";
$q_pend = mysqli_query($koneksi, $sql_pend); // Otomatis tambah ke Data Penduduk
$d_pend = mysqli_fetch_array($q_pend); // Ambil data alamat dari pelapor
$id_pelapor = $_POST['pelapor'];
if (!$d_pend) { $sql_pelapor = "SELECT desa, rt, rw FROM tb_pdd WHERE id_pend='$id_pelapor'";
die("Data penduduk tidak ditemukan. Silakan pilih penduduk yang valid."); $q_pelapor = mysqli_query($koneksi, $sql_pelapor);
} $d_pelapor = mysqli_fetch_array($q_pelapor);
$desa = $d_pelapor['desa'];
$nik = mysqli_real_escape_string($koneksi, trim($d_pend['nik'])); $rt = $d_pelapor['rt'];
$nama_datang = mysqli_real_escape_string($koneksi, trim($d_pend['nama'])); $rw = $d_pelapor['rw'];
$jekel = mysqli_real_escape_string($koneksi, trim($d_pend['jekel']));
$sql_pdd = "INSERT INTO tb_pdd (nik, nama, tempat_lh, tgl_lh, jekel, desa, rt, rw, agama, kawin, pekerjaan, status) VALUES (
$sql_simpan = "INSERT INTO tb_datang (id_pend, nik, nama_datang, jekel, tgl_datang, pelapor) VALUES ( '".$_POST['nik']."',
'$id_pend', '".$_POST['nama_datang']."',
'$nik', '-',
'$nama_datang', '0000-00-00',
'$jekel', '".$_POST['jekel']."',
'$tgl_datang', '$desa', '$rt', '$rw',
'$pelapor')"; '-', '-', '-', 'Ada')";
$query_simpan = mysqli_query($koneksi, $sql_simpan); $query_pdd = mysqli_query($koneksi, $sql_pdd);
mysqli_close($koneksi); mysqli_close($koneksi);
if ($query_simpan) { if ($query_simpan && $query_pdd) {
echo "<script> echo "<script>
Swal.fire({title: 'Tambah Data Berhasil',text: '',icon: 'success',confirmButtonText: 'OK' Swal.fire({title: 'Tambah Data Berhasil',text: '',icon: 'success',confirmButtonText: 'OK'
}).then((result) => {if (result.value){ }).then((result) => {if (result.value){
window.location = 'index.php?page=data-datang'; window.location = 'index.php?page=data-datang';
} }
})</script>"; })</script>";
}else{ }else{
echo "<script> echo "<script>
Swal.fire({title: 'Tambah Data Gagal',text: '',icon: 'error',confirmButtonText: 'OK' Swal.fire({title: 'Tambah Data Gagal',text: '',icon: 'error',confirmButtonText: 'OK'
}).then((result) => {if (result.value){ }).then((result) => {if (result.value){
window.location = 'index.php?page=add-datang'; window.location = 'index.php?page=add-datang';
} }
})</script>"; })</script>";
}} }}
//selesai proses simpan data //selesai proses simpan data

43
admin/datang/data_datang.php Executable file → Normal file
View File

@@ -17,7 +17,7 @@
<th>No</th> <th>No</th>
<th>NIK</th> <th>NIK</th>
<th>Nama</th> <th>Nama</th>
<th>Jenis Kelamin</th> <th>Jekel</th>
<th>Tanggal</th> <th>Tanggal</th>
<th>Pelapor</th> <th>Pelapor</th>
<th>Aksi</th> <th>Aksi</th>
@@ -43,15 +43,7 @@
<?php echo $data['nama_datang']; ?> <?php echo $data['nama_datang']; ?>
</td> </td>
<td> <td>
<?php <?php echo $data['jekel']; ?>
if ($data['jekel'] == 'LK') {
echo 'LAKI-LAKI';
} elseif ($data['jekel'] == 'PR') {
echo 'PEREMPUAN';
} else {
echo $data['jekel'];
}
?>
</td> </td>
<td> <td>
<?php echo $data['tgl_datang']; ?> <?php echo $data['tgl_datang']; ?>
@@ -64,10 +56,10 @@
class="btn btn-success btn-sm"> class="btn btn-success btn-sm">
<i class="fa fa-edit"></i> <i class="fa fa-edit"></i>
</a> </a>
<a href="?page=del-datang&kode=<?php echo $data['id_datang']; ?>" onclick="confirmDelete(event)" <a href="?page=del-datang&kode=<?php echo $data['id_datang']; ?>" onclick="return confirm('Apakah anda yakin hapus data ini ?')"
title="Hapus" class="btn btn-danger btn-sm"> title="Hapus" class="btn btn-danger btn-sm">
<i class="fa fa-trash"></i> <i class="fa fa-trash"></i>
</a> </>
</td> </td>
</tr> </tr>
@@ -76,26 +68,7 @@
?> ?>
</tbody> </tbody>
</tfoot> </tfoot>
</table> </table>
</div> </div>
</div> </div>
<!-- /.card-body --> <!-- /.card-body -->
<script>
function confirmDelete(event) {
event.preventDefault();
Swal.fire({
title: 'Konfirmasi Hapus',
text: 'Apakah Anda yakin ingin menghapus data ini?',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Ya, Hapus',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = event.currentTarget.href;
}
});
}
</script>

0
admin/datang/del_datang.php Executable file → Normal file
View File

173
admin/datang/edit_datang.php Executable file → Normal file
View File

@@ -1,14 +1,11 @@
<?php <?php
if(isset($_GET['kode'])){ if(isset($_GET['kode'])){
$sql_cek = "SELECT d.id_datang, d.id_pend, d.nik, d.nama_datang, d.jekel, d.tgl_datang, p.id_pend as id_pelapor, p.nama as nama_pelapor, pd.tgl_lh $sql_cek = "SELECT d.id_datang, d.nik, d.nama_datang, d.jekel, d.tgl_datang, p.id_pend, p.nama from
FROM tb_datang d tb_datang d inner join tb_pdd p on d.pelapor=p.id_pend WHERE id_datang='".$_GET['kode']."'";
INNER JOIN tb_pdd p ON d.pelapor=p.id_pend $query_cek = mysqli_query($koneksi, $sql_cek);
LEFT JOIN tb_pdd pd ON d.id_pend=pd.id_pend $data_cek = mysqli_fetch_array($query_cek,MYSQLI_BOTH);
WHERE d.id_datang='".$_GET['kode']."'"; }
$query_cek = mysqli_query($koneksi, $sql_cek);
$data_cek = mysqli_fetch_array($query_cek,MYSQLI_BOTH);
}
?> ?>
<div class="card card-success"> <div class="card card-success">
@@ -27,51 +24,43 @@
</div> </div>
</div> </div>
<input type="hidden" id="id_pend" name="id_pend" value="<?php echo isset($data_cek['id_pend']) ? $data_cek['id_pend'] : ''; ?>"> <div class="form-group row">
<label class="col-sm-2 col-form-label">NIK</label>
<div class="form-group row"> <div class="col-sm-6">
<label class="col-sm-2 col-form-label">NIK</label> <input type="text" class="form-control" id="nik" name="nik" value="<?php echo $data_cek['nik']; ?>"
<div class="col-sm-6"> required>
<input type="text" class="form-control" value="<?php echo htmlspecialchars($data_cek['nik'], ENT_QUOTES); ?>" readonly> </div>
<input type="hidden" name="nik" value="<?php echo htmlspecialchars($data_cek['nik'], ENT_QUOTES); ?>"> </div>
</div>
</div> <div class="form-group row">
<label class="col-sm-2 col-form-label">Nama</label>
<div class="form-group row"> <div class="col-sm-6">
<label class="col-sm-2 col-form-label">Nama</label> <input type="text" class="form-control" id="nama_datang" name="nama_datang" value="<?php echo $data_cek['nama_datang']; ?>"
<div class="col-sm-6"> required>
<input type="text" class="form-control" value="<?php echo htmlspecialchars($data_cek['nama_datang'], ENT_QUOTES); ?>" readonly> </div>
<input type="hidden" name="nama_datang" value="<?php echo htmlspecialchars($data_cek['nama_datang'], ENT_QUOTES); ?>"> </div>
</div>
</div> <div class="form-group row">
<label class="col-sm-2 col-form-label">Jenis Kelamin</label>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Jenis Kelamin</label>
<div class="col-sm-3">
<input type="text" class="form-control" value="<?php
$display_jekel = $data_cek['jekel'];
if ($display_jekel == 'LK') {
$display_jekel = 'LAKI-LAKI';
} elseif ($display_jekel == 'PR') {
$display_jekel = 'PEREMPUAN';
}
echo htmlspecialchars($display_jekel, ENT_QUOTES);
?>" readonly>
<input type="hidden" name="jekel" value="<?php echo htmlspecialchars($data_cek['jekel'], ENT_QUOTES); ?>">
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Tgl Lahir</label>
<div class="col-sm-3">
<input type="text" class="form-control" value="<?php echo htmlspecialchars(isset($data_cek['tgl_lh']) ? $data_cek['tgl_lh'] : '-', ENT_QUOTES); ?>" readonly>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Tgl Datang</label>
<div class="col-sm-3"> <div class="col-sm-3">
<input type="date" class="form-control" id="tgl_datang" name="tgl_datang" value="<?php echo htmlspecialchars($data_cek['tgl_datang'], ENT_QUOTES); ?>" <select name="jekel" id="jekel" class="form-control">
<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>";
if ($data_cek['jekel'] == "PR") echo "<option value='PR' selected>PR</option>";
else echo "<option value='PR'>PR</option>";
?>
</select>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Tgl Datang</label>
<div class="col-sm-3">
<input type="date" class="form-control" id="tgl_datang" name="tgl_datang" value="<?php echo $data_cek['tgl_datang']; ?>"
required> required>
</div> </div>
</div> </div>
@@ -83,14 +72,15 @@
<option selected="">- Pilih -</option> <option selected="">- Pilih -</option>
<?php <?php
// ambil data dari database // ambil data dari database
$query = "select * from tb_pdd where status='Ada'"; $query = "select * from tb_pdd";
$hasil = mysqli_query($koneksi, $query); $hasil = mysqli_query($koneksi, $query);
while ($row = mysqli_fetch_array($hasil)) { while ($row = mysqli_fetch_array($hasil)) {
?> ?>
<option value="<?php echo $row['id_pend'] ?>" <?=$data_cek['id_pelapor']==$row['id_pend'] ? "selected" : null ?>> <option value="<?php echo $row['id_pend'] ?>" <?=$data_cek[
<?php echo htmlspecialchars($row['nik'], ENT_QUOTES); ?> 'id_pend']==$row[ 'id_pend'] ? "selected" : null ?>>
<?php echo $row['nik'] ?>
- -
<?php echo htmlspecialchars($row['nama'], ENT_QUOTES); ?> <?php echo $row['nama'] ?>
</option> </option>
<?php <?php
} }
@@ -108,44 +98,31 @@
</form> </form>
</div> </div>
<?php <?php
if (isset ($_POST['Ubah'])){ if (isset ($_POST['Ubah'])){
// Sanitize Input to prevent SQL Injection & Syntax Errors $sql_ubah = "UPDATE tb_datang SET
$id_datang = (int)$_POST['id_datang']; nik='".$_POST['nik']."',
$nik = mysqli_real_escape_string($koneksi, trim($_POST['nik'])); nama_datang='".$_POST['nama_datang']."',
$nama_datang = mysqli_real_escape_string($koneksi, trim($_POST['nama_datang'])); jekel='".$_POST['jekel']."',
$jekel = mysqli_real_escape_string($koneksi, trim($_POST['jekel'])); tgl_datang='".$_POST['tgl_datang']."',
pelapor='".$_POST['pelapor']."'
$tgl_datang = mysqli_real_escape_string($koneksi, trim($_POST['tgl_datang'])); WHERE id_datang='".$_POST['id_datang']."'";
$pelapor = (int)$_POST['pelapor']; $query_ubah = mysqli_query($koneksi, $sql_ubah);
mysqli_close($koneksi);
// Update tb_datang table
$sql_ubah = "UPDATE tb_datang SET if ($query_ubah) {
nik='$nik', echo "<script>
nama_datang='$nama_datang', Swal.fire({title: 'Ubah Data Berhasil',text: '',icon: 'success',confirmButtonText: 'OK'
jekel='$jekel', }).then((result) => {if (result.value)
tgl_datang='$tgl_datang', {window.location = 'index.php?page=data-datang';
pelapor='$pelapor' }
WHERE id_datang='$id_datang'"; })</script>";
$query_ubah = mysqli_query($koneksi, $sql_ubah); }else{
echo "<script>
Swal.fire({title: 'Ubah Data Gagal',text: '',icon: 'error',confirmButtonText: 'OK'
}).then((result) => {if (result.value)
mysqli_close($koneksi); {window.location = 'index.php?page=data-datang';
}
if ($query_ubah) { })</script>";
echo "<script> }}
Swal.fire({title: 'Ubah Data Berhasil',text: '',icon: 'success',confirmButtonText: 'OK'
}).then((result) => {if (result.value)
{window.location = 'index.php?page=data-datang';
}
})</script>";
}else{
echo "<script>
Swal.fire({title: 'Ubah Data Gagal',text: '',icon: 'error',confirmButtonText: 'OK'
}).then((result) => {if (result.value)
{window.location = 'index.php?page=data-datang';
}
})</script>";
}}

198
admin/kartu/add_kartu.php Executable file → Normal file
View File

@@ -30,9 +30,9 @@
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-2 col-form-label">Kepala Keluarga</label> <label class="col-sm-2 col-form-label">Kpl Keluarga</label>
<div class="col-sm-6"> <div class="col-sm-6">
<input type="text" class="form-control" id="kepala" name="kepala" placeholder="Kepala Keluarga" required> <input type="text" class="form-control" id="kepala" name="kepala" placeholder="Kpl Keluarga" required>
</div> </div>
</div> </div>
@@ -46,10 +46,10 @@
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-2 col-form-label">RT/RW</label> <label class="col-sm-2 col-form-label">RT/RW</label>
<div class="col-sm-3"> <div class="col-sm-3">
<input type="text" class="form-control" id="rt" name="rt" placeholder="RT" value="000" required> <input type="text" class="form-control" id="rt" name="rt" placeholder="RT" required>
</div> </div>
<div class="col-sm-3"> <div class="col-sm-3">
<input type="text" class="form-control" id="rw" name="rw" placeholder="RW" value="000" required> <input type="text" class="form-control" id="rw" name="rw" placeholder="RW" required>
</div> </div>
</div> </div>
@@ -166,18 +166,7 @@ window.addEventListener('load', function() {
<tr><td>Kabupaten</td><td>${d.kabupaten || '-'}</td></tr> <tr><td>Kabupaten</td><td>${d.kabupaten || '-'}</td></tr>
<tr><td>Provinsi</td><td>${d.provinsi || '-'}</td></tr> <tr><td>Provinsi</td><td>${d.provinsi || '-'}</td></tr>
</table> </table>
<p class="mb-0 text-muted">Gunakan data ini?</p>
<hr>
<strong>Ditemukan ${d.anggota ? d.anggota.length : 0} Anggota:</strong>
<div style="max-height: 150px; overflow-y: auto; background: #f8f9fa; border: 1px solid #ddd; padding: 5px;">
${d.anggota && d.anggota.length > 0 ?
'<ul style="padding-left: 20px; margin-bottom: 0;">' +
d.anggota.map(m => `<li><b>${m.nama}</b><br><small>${m.nik} (${m.hubungan})</small></li>`).join('') +
'</ul>'
: '<i class="text-muted">Tidak ada anggota terdeteksi</i>'}
</div>
<p class="mb-0 text-muted mt-2">Gunakan data ini?</p>
</div> </div>
`, `,
icon: 'question', icon: 'question',
@@ -190,8 +179,8 @@ window.addEventListener('load', function() {
if(d.anggota) document.getElementById('anggota_json').value = JSON.stringify(d.anggota); 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.kepala_keluarga) document.getElementsByName('kepala')[0].value = d.kepala_keluarga;
if(d.desa) document.getElementsByName('desa')[0].value = d.desa; if(d.desa) document.getElementsByName('desa')[0].value = d.desa;
if(d.rt) document.getElementsByName('rt')[0].value = d.rt; document.getElementsByName('rt')[0].value = d.rt || '000';
if(d.rw) document.getElementsByName('rw')[0].value = d.rw; document.getElementsByName('rw')[0].value = d.rw || '000';
if(d.kecamatan) document.getElementsByName('kec')[0].value = d.kecamatan; if(d.kecamatan) document.getElementsByName('kec')[0].value = d.kecamatan;
if(d.kabupaten) document.getElementsByName('kab')[0].value = d.kabupaten; if(d.kabupaten) document.getElementsByName('kab')[0].value = d.kabupaten;
if(d.provinsi) document.getElementsByName('prov')[0].value = d.provinsi; if(d.provinsi) document.getElementsByName('prov')[0].value = d.provinsi;
@@ -238,28 +227,27 @@ window.addEventListener('load', function() {
} }
//mulai proses simpan data //mulai proses simpan data
$no_kk_raw = trim($_POST['no_kk']); $no_kk = $_POST['no_kk'];
$no_kk_clean = mysqli_real_escape_string($koneksi, $no_kk_raw); $cek_kk = mysqli_query($koneksi, "SELECT * FROM tb_kk WHERE no_kk='$no_kk'");
$cek_kk = mysqli_query($koneksi, "SELECT * FROM tb_kk WHERE no_kk='$no_kk_clean'"); if(mysqli_num_rows($cek_kk) > 0){
if(mysqli_num_rows($cek_kk) > 0){ echo "<script>
echo "<script> Swal.fire({title: 'Gagal',text: 'No KK sudah terdaftar dalam sistem!',icon: 'error',confirmButtonText: 'OK'
Swal.fire({title: 'Gagal',text: 'No KK sudah terdaftar dalam sistem!',icon: 'error',confirmButtonText: 'OK' }).then((result) => {if (result.value){
}).then((result) => {if (result.value){ window.location = 'index.php?page=add-kartu';
window.location = 'index.php?page=add-kartu'; }
} })</script>";
})</script>"; return;
return; }
}
// Sanitize Inputs
// Sanitize Inputs $no_kk = mysqli_real_escape_string($koneksi, $_POST['no_kk']);
$no_kk = $no_kk_clean; $kepala = mysqli_real_escape_string($koneksi, $_POST['kepala']);
$kepala = mysqli_real_escape_string($koneksi, trim($_POST['kepala'])); $desa = mysqli_real_escape_string($koneksi, $_POST['desa']);
$desa = mysqli_real_escape_string($koneksi, trim($_POST['desa'])); $rt = mysqli_real_escape_string($koneksi, $_POST['rt']);
$rt = mysqli_real_escape_string($koneksi, trim($_POST['rt'])); $rw = mysqli_real_escape_string($koneksi, $_POST['rw']);
$rw = mysqli_real_escape_string($koneksi, trim($_POST['rw'])); $kec = mysqli_real_escape_string($koneksi, $_POST['kec']);
$kec = mysqli_real_escape_string($koneksi, trim($_POST['kec'])); $kab = mysqli_real_escape_string($koneksi, $_POST['kab']);
$kab = mysqli_real_escape_string($koneksi, trim($_POST['kab'])); $prov = mysqli_real_escape_string($koneksi, $_POST['prov']);
$prov = mysqli_real_escape_string($koneksi, trim($_POST['prov']));
$sql_simpan = "INSERT INTO tb_kk (no_kk, kepala, desa, rt, rw, kec, kab, prov, foto_kk) VALUES ( $sql_simpan = "INSERT INTO tb_kk (no_kk, kepala, desa, rt, rw, kec, kab, prov, foto_kk) VALUES (
'$no_kk', '$no_kk',
@@ -273,70 +261,80 @@ window.addEventListener('load', function() {
'$nama_file')"; '$nama_file')";
$query_simpan = mysqli_query($koneksi, $sql_simpan); $query_simpan = mysqli_query($koneksi, $sql_simpan);
// Process Auto-Linking Members // Process Auto-Linking Members
$linked_count = 0; if ($query_simpan && !empty($_POST['anggota_json'])) {
$failed_count = 0; file_put_contents('debug_log.txt', "----------------------------------------\n", FILE_APPEND);
$failed_members = []; // Store failed members for detailed feedback file_put_contents('debug_log.txt', "Saving KK ID: " . mysqli_insert_id($koneksi) . "\n", FILE_APPEND);
file_put_contents('debug_log.txt', "Raw JSON: " . $_POST['anggota_json'] . "\n", FILE_APPEND);
if ($query_simpan && !empty($_POST['anggota_json'])) {
$id_kk_baru = mysqli_insert_id($koneksi); $id_kk_baru = mysqli_insert_id($koneksi);
$anggota_list = json_decode($_POST['anggota_json'], true); $anggota_list = json_decode($_POST['anggota_json'], true);
if (is_array($anggota_list)) { if (is_array($anggota_list)) {
foreach ($anggota_list as $mem) { foreach ($anggota_list as $mem) {
$nik_mem = mysqli_real_escape_string($koneksi, $mem['nik']); $nik_mem = mysqli_real_escape_string($koneksi, $mem['nik']);
$nama_mem = isset($mem['nama']) ? mysqli_real_escape_string($koneksi, $mem['nama']) : ''; $hub_mem = mysqli_real_escape_string($koneksi, $mem['hubungan']);
$hub_mem = mysqli_real_escape_string($koneksi, $mem['hubungan']);
file_put_contents('debug_log.txt', "Processing NIK: $nik_mem\n", FILE_APPEND);
// Search Resident by NIK
$sql_cek_pend = "SELECT id_pend FROM tb_pdd WHERE nik='$nik_mem'"; // Search Resident by NIK
$q_cek_pend = mysqli_query($koneksi, $sql_cek_pend); $sql_cek_pend = "SELECT id_pend FROM tb_pdd WHERE nik='$nik_mem'";
if ($row_pend = mysqli_fetch_assoc($q_cek_pend)) { $q_cek_pend = mysqli_query($koneksi, $sql_cek_pend);
$id_pend_found = $row_pend['id_pend'];
if ($row_pend = mysqli_fetch_assoc($q_cek_pend)) {
// Insert into tb_anggota $id_pend_found = $row_pend['id_pend'];
$sql_add_ang = "INSERT INTO tb_anggota (id_kk, id_pend, hubungan) VALUES ('$id_kk_baru', '$id_pend_found', '$hub_mem')"; file_put_contents('debug_log.txt', "Found Existing ID: $id_pend_found. Linking...\n", FILE_APPEND);
mysqli_query($koneksi, $sql_add_ang);
$linked_count++; // Insert into tb_anggota
} else { $sql_add_ang = "INSERT INTO tb_anggota (id_kk, id_pend, hubungan) VALUES ('$id_kk_baru', '$id_pend_found', '$hub_mem')";
$failed_count++; if (!mysqli_query($koneksi, $sql_add_ang)) {
$failed_members[] = ['nik' => $nik_mem, 'nama' => $nama_mem, 'hubungan' => $hub_mem]; file_put_contents('debug_log.txt', "Link Error: " . mysqli_error($koneksi) . "\n", FILE_APPEND);
} } else {
} file_put_contents('debug_log.txt', "Success Link ID $id_pend_found\n", FILE_APPEND);
} }
} } else {
// Create New Resident if not found
file_put_contents('debug_log.txt', "NIK Not Found. Creating New.\n", FILE_APPEND);
$nama_mem = mysqli_real_escape_string($koneksi, $mem['nama']);
$tempat_lh = mysqli_real_escape_string($koneksi, $mem['tempat_lh'] ?? '-');
$tgl_lh = mysqli_real_escape_string($koneksi, $mem['tgl_lh'] ?? '0000-00-00');
$jekel = mysqli_real_escape_string($koneksi, $mem['jekel'] ?? 'LK');
$agama = mysqli_real_escape_string($koneksi, $mem['agama'] ?? 'Islam');
$kawin = mysqli_real_escape_string($koneksi, $mem['kawin'] ?? 'Belum Kawin');
$pekerjaan = mysqli_real_escape_string($koneksi, $mem['pekerjaan'] ?? 'Pelajar/Mahasiswa');
$kewarganegaraan = mysqli_real_escape_string($koneksi, $mem['kewarganegaraan'] ?? 'WNI');
$sql_new_pend = "INSERT INTO tb_pdd (
nik, nama, tempat_lh, tgl_lh, jekel,
desa, rt, rw, agama, kawin, pekerjaan, status, kwn
) VALUES (
'$nik_mem', '$nama_mem', '$tempat_lh', '$tgl_lh', '$jekel',
'$desa', '$rt', '$rw', '$agama', '$kawin', '$pekerjaan', 'Ada', '$kewarganegaraan'
)";
if (mysqli_query($koneksi, $sql_new_pend)) {
$id_pend_new = mysqli_insert_id($koneksi);
file_put_contents('debug_log.txt', "Created New ID: $id_pend_new. Linking...\n", FILE_APPEND);
// Link to KK
$sql_add_ang = "INSERT INTO tb_anggota (id_kk, id_pend, hubungan) VALUES ('$id_kk_baru', '$id_pend_new', '$hub_mem')";
mysqli_query($koneksi, $sql_add_ang);
} else {
file_put_contents('debug_log.txt', "Error Creating Resident: " . mysqli_error($koneksi) . "\n", FILE_APPEND);
}
}
}
} else {
file_put_contents('debug_log.txt', "JSON Decode Failed or Not Array\n", FILE_APPEND);
}
}
mysqli_close($koneksi); mysqli_close($koneksi);
if ($query_simpan) { if ($query_simpan) {
$msg_add = "";
if($linked_count > 0 || $failed_count > 0) {
$msg_add = "<br>Anggota Terhubung: <b>$linked_count</b><br>Tidak Ditemukan: <b>$failed_count</b>";
// Add detailed failed members list if any
if (!empty($failed_members)) {
$msg_add .= "<br><br><b>Detail Anggota Tidak Ditemukan:</b><br>";
$msg_add .= "<div style='max-height: 150px; overflow-y: auto; background: #f8f9fa; border: 1px solid #ddd; padding: 5px; font-size: 0.9rem;'>";
foreach ($failed_members as $fm) {
$url_params = http_build_query([
'nik' => $fm['nik'],
'nama' => $fm['nama'],
'desa' => $desa,
'rt' => $rt,
'rw' => $rw,
'kecamatan' => $kec,
'kabupaten' => $kab,
'provinsi' => $prov
]);
$add_link = "<a href='index.php?page=add-pend&$url_params' class='btn btn-xs btn-outline-primary ml-2'>Tambah</a>";
$msg_add .= "• <b>" . htmlspecialchars($fm['nik']) . "</b> - " . htmlspecialchars($fm['nama']) . " (" . htmlspecialchars($fm['hubungan']) . ") $add_link<br>";
}
$msg_add .= "</div>";
$msg_add .= "<small class='text-muted'>Tambahkan data penduduk yang belum terdaftar melalui menu <a href='index.php?page=add-pend'>Tambah Penduduk</a>.</small>";
}
}
echo "<script> echo "<script>
Swal.fire({title: 'Tambah Data Berhasil',html: 'Data KK disimpan.$msg_add',icon: 'success',confirmButtonText: 'OK' Swal.fire({title: 'Tambah Data Berhasil',text: '',icon: 'success',confirmButtonText: 'OK'
}).then((result) => {if (result.value){ }).then((result) => {if (result.value){
window.location = 'index.php?page=data-kartu'; window.location = 'index.php?page=data-kartu';
} }

43
admin/kartu/anggota.php Executable file → Normal file
View File

@@ -97,7 +97,7 @@
<tr> <tr>
<th>NIK</th> <th>NIK</th>
<th>Nama</th> <th>Nama</th>
<th>Jenis Kelamin</th> <th>Jekel</th>
<th>Hub Keluarga</th> <th>Hub Keluarga</th>
<th>Aksi</th> <th>Aksi</th>
</tr> </tr>
@@ -119,21 +119,13 @@
<?php echo $data['nama']; ?> <?php echo $data['nama']; ?>
</td> </td>
<td> <td>
<?php <?php echo $data['jekel']; ?>
if ($data['jekel'] == 'LK') {
echo 'LAKI-LAKI';
} elseif ($data['jekel'] == 'PR') {
echo 'PEREMPUAN';
} else {
echo $data['jekel'];
}
?>
</td> </td>
<td> <td>
<?php echo $data['hubungan']; ?> <?php echo $data['hubungan']; ?>
</td> </td>
<td> <td>
<a href="?page=del-anggota&kode=<?php echo $data['id_anggota']; ?>" onclick="confirmDelete(event)" <a href="?page=del-anggota&kode=<?php echo $data['id_anggota']; ?>" onclick="return confirm('Apakah anda yakin hapus data ini ?')"
title="Hapus" class="btn btn-danger btn-sm"> title="Hapus" class="btn btn-danger btn-sm">
<i class="fa fa-trash"></i> <i class="fa fa-trash"></i>
</a> </a>
@@ -143,30 +135,11 @@
<?php <?php
} }
?> ?>
</tbody> </tbody>
</tfoot> </tfoot>
</table> </table>
</div> </div>
</div> </div>
<script>
function confirmDelete(event) {
event.preventDefault();
Swal.fire({
title: 'Konfirmasi Hapus',
text: 'Apakah Anda yakin ingin menghapus data ini?',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Ya, Hapus',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = event.currentTarget.href;
}
});
}
</script>
</div> </div>
<div class="card-footer"> <div class="card-footer">
<a href="?page=data-kartu" title="Kembali" class="btn btn-warning">Kembali</a> <a href="?page=data-kartu" title="Kembali" class="btn btn-warning">Kembali</a>

12
admin/kartu/anggota_full.php Executable file → Normal file
View File

@@ -59,7 +59,7 @@
<th>No</th> <th>No</th>
<th>NIK</th> <th>NIK</th>
<th>Nama</th> <th>Nama</th>
<th>Jenis Kelamin</th> <th>Jekel</th>
<th>Hub Keluarga</th> <th>Hub Keluarga</th>
<th>Status</th> <th>Status</th>
</tr> </tr>
@@ -85,15 +85,7 @@
<?php echo $data['nama']; ?> <?php echo $data['nama']; ?>
</td> </td>
<td> <td>
<?php <?php echo $data['jekel']; ?>
if ($data['jekel'] == 'LK') {
echo 'LAKI-LAKI';
} elseif ($data['jekel'] == 'PR') {
echo 'PEREMPUAN';
} else {
echo $data['jekel'];
}
?>
</td> </td>
<td> <td>
<?php echo $data['hubungan']; ?> <?php echo $data['hubungan']; ?>

0
admin/kartu/data_kartu.php Executable file → Normal file
View File

0
admin/kartu/del_anggota.php Executable file → Normal file
View File

0
admin/kartu/del_kartu.php Executable file → Normal file
View File

6
admin/kartu/edit_kartu.php Executable file → Normal file
View File

@@ -52,7 +52,7 @@
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-2 col-form-label">Kepala Keluarga</label> <label class="col-sm-2 col-form-label">Kpl Keluarga</label>
<div class="col-sm-6"> <div class="col-sm-6">
<input type="text" class="form-control" id="kepala" name="kepala" value="<?php echo $data_cek['kepala']; ?>" <input type="text" class="form-control" id="kepala" name="kepala" value="<?php echo $data_cek['kepala']; ?>"
required> required>
@@ -208,8 +208,8 @@ window.addEventListener('load', function() {
if(d.no_kk) document.getElementsByName('no_kk')[0].value = d.no_kk; 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.kepala_keluarga) document.getElementsByName('kepala')[0].value = d.kepala_keluarga;
if(d.desa) document.getElementsByName('desa')[0].value = d.desa; if(d.desa) document.getElementsByName('desa')[0].value = d.desa;
if(d.rt) document.getElementsByName('rt')[0].value = d.rt; document.getElementsByName('rt')[0].value = d.rt || '000';
if(d.rw) document.getElementsByName('rw')[0].value = d.rw; document.getElementsByName('rw')[0].value = d.rw || '000';
if(d.kecamatan) document.getElementsByName('kec')[0].value = d.kecamatan; if(d.kecamatan) document.getElementsByName('kec')[0].value = d.kecamatan;
if(d.kabupaten) document.getElementsByName('kab')[0].value = d.kabupaten; if(d.kabupaten) document.getElementsByName('kab')[0].value = d.kabupaten;
if(d.provinsi) document.getElementsByName('prov')[0].value = d.provinsi; if(d.provinsi) document.getElementsByName('prov')[0].value = d.provinsi;

145
admin/lahir/add_lahir.php Executable file → Normal file
View File

@@ -27,16 +27,16 @@
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-2 col-form-label">Jenis Kelamin</label> <label class="col-sm-2 col-form-label">Jenis Kelain</label>
<div class="col-sm-3"> <div class="col-sm-3">
<select name="jekel" id="jekel" class="form-control"> <select name="jekel" id="jekel" class="form-control">
<option>- Pilih -</option> <option>- Pilih -</option>
<option>LK</option> <option>LK</option>
<option>PR</option> <option>PR</option>
</select> </select>
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-2 col-form-label">Keluarga</label> <label class="col-sm-2 col-form-label">Keluarga</label>
@@ -69,69 +69,62 @@
</form> </form>
</div> </div>
<?php <?php
if (isset ($_POST['Simpan'])){ if (isset ($_POST['Simpan'])){
//mulai proses simpan data //mulai proses simpan data
$sql_simpan = "INSERT INTO tb_lahir (nama, tgl_lh, jekel, id_kk) VALUES (
// Sanitize Input to prevent SQL Injection & Syntax Errors '".$_POST['nama']."',
$nik = mysqli_real_escape_string($koneksi, trim($_POST['nik'])); '".$_POST['tgl_lh']."',
$nama = mysqli_real_escape_string($koneksi, trim($_POST['nama'])); '".$_POST['jekel']."',
$tgl_lh = mysqli_real_escape_string($koneksi, trim($_POST['tgl_lh'])); '".$_POST['id_kk']."')";
$jekel = mysqli_real_escape_string($koneksi, trim($_POST['jekel'])); $query_simpan = mysqli_query($koneksi, $sql_simpan);
$id_kk = (int)$_POST['id_kk']; // Cast to integer for safety
// Otomatis tambah ke Data Penduduk
$sql_simpan = "INSERT INTO tb_lahir (nama, tgl_lh, jekel, id_kk) VALUES ( // Ambil data alamat dari KK
'$nama', $id_kk = $_POST['id_kk'];
'$tgl_lh', $sql_kk = "SELECT desa, rt, rw FROM tb_kk WHERE id_kk='$id_kk'";
'$jekel', $q_kk = mysqli_query($koneksi, $sql_kk);
'$id_kk')"; $d_kk = mysqli_fetch_array($q_kk);
$query_simpan = mysqli_query($koneksi, $sql_simpan); $desa = $d_kk['desa'];
$rt = $d_kk['rt'];
// Otomatis tambah ke Data Penduduk $rw = $d_kk['rw'];
// Ambil data alamat dari KK
$sql_kk = "SELECT desa, rt, rw FROM tb_kk WHERE id_kk='$id_kk'"; $sql_pdd = "INSERT INTO tb_pdd (nik, nama, tempat_lh, tgl_lh, jekel, desa, rt, rw, agama, kawin, pekerjaan, status) VALUES (
$q_kk = mysqli_query($koneksi, $sql_kk); '".$_POST['nik']."',
$d_kk = mysqli_fetch_array($q_kk); '".$_POST['nama']."',
$desa = $d_kk['desa']; '-',
$rt = $d_kk['rt']; '".$_POST['tgl_lh']."',
$rw = $d_kk['rw']; '".$_POST['jekel']."',
'$desa', '$rt', '$rw',
$sql_pdd = "INSERT INTO tb_pdd (nik, nama, tempat_lh, tgl_lh, jekel, desa, rt, rw, agama, kawin, pekerjaan, status) VALUES ( '-', 'Belum', 'Belum/Tidak Bekerja', 'Ada')";
'$nik', $query_pdd = mysqli_query($koneksi, $sql_pdd);
'$nama',
'-', // Ambil ID Penduduk yang baru dibuat
'$tgl_lh', $id_pend_baru = mysqli_insert_id($koneksi);
'$jekel',
'$desa', '$rt', '$rw', // Masukkan ke Anggota KK
'-', 'Belum', 'Belum/Tidak Bekerja', 'Ada')"; $sql_anggota = "INSERT INTO tb_anggota (id_kk, id_pend, hubungan) VALUES (
$query_pdd = mysqli_query($koneksi, $sql_pdd); '$id_kk',
'$id_pend_baru',
// Ambil ID Penduduk yang baru dibuat 'Anak')";
$id_pend_baru = mysqli_insert_id($koneksi); $query_anggota = mysqli_query($koneksi, $sql_anggota);
// Masukkan ke Anggota KK mysqli_close($koneksi);
$sql_anggota = "INSERT INTO tb_anggota (id_kk, id_pend, hubungan) VALUES (
'$id_kk', if ($query_simpan && $query_pdd && $query_anggota) {
'$id_pend_baru', echo "<script>
'Anak')"; Swal.fire({title: 'Tambah Data Berhasil',text: '',icon: 'success',confirmButtonText: 'OK'
$query_anggota = mysqli_query($koneksi, $sql_anggota); }).then((result) => {if (result.value){
window.location = 'index.php?page=data-lahir';
mysqli_close($koneksi); }
})</script>";
if ($query_simpan && $query_pdd && $query_anggota) { }else{
echo "<script> echo "<script>
Swal.fire({title: 'Tambah Data Berhasil',text: '',icon: 'success',confirmButtonText: 'OK' Swal.fire({title: 'Tambah Data Gagal',text: '',icon: 'error',confirmButtonText: 'OK'
}).then((result) => {if (result.value){ }).then((result) => {if (result.value){
window.location = 'index.php?page=data-lahir'; window.location = 'index.php?page=add-lahir';
} }
})</script>"; })</script>";
}else{ }}
echo "<script> //selesai proses simpan data
Swal.fire({title: 'Tambah Data Gagal',text: '',icon: 'error',confirmButtonText: 'OK'
}).then((result) => {if (result.value){
window.location = 'index.php?page=add-lahir';
}
})</script>";
}}
//selesai proses simpan data

43
admin/lahir/data_lahir.php Executable file → Normal file
View File

@@ -17,7 +17,7 @@
<th>No</th> <th>No</th>
<th>Nama</th> <th>Nama</th>
<th>Tgl Lahir</th> <th>Tgl Lahir</th>
<th>Jenis Kelamin</th> <th>Jekel</th>
<th>Keluarga</th> <th>Keluarga</th>
<th>Aksi</th> <th>Aksi</th>
</tr> </tr>
@@ -42,15 +42,7 @@
<?php echo $data['tgl_lh']; ?> <?php echo $data['tgl_lh']; ?>
</td> </td>
<td> <td>
<?php <?php echo $data['jekel']; ?>
if ($data['jekel'] == 'LK') {
echo 'LAKI-LAKI';
} elseif ($data['jekel'] == 'PR') {
echo 'PEREMPUAN';
} else {
echo $data['jekel'];
}
?>
</td> </td>
<td> <td>
<?php echo $data['no_kk']; ?>- <?php echo $data['no_kk']; ?>-
@@ -61,10 +53,10 @@
class="btn btn-success btn-sm"> class="btn btn-success btn-sm">
<i class="fa fa-edit"></i> <i class="fa fa-edit"></i>
</a> </a>
<a href="?page=del-lahir&kode=<?php echo $data['id_lahir']; ?>" onclick="confirmDelete(event)" <a href="?page=del-lahir&kode=<?php echo $data['id_lahir']; ?>" onclick="return confirm('Apakah anda yakin hapus data ini ?')"
title="Hapus" class="btn btn-danger btn-sm"> title="Hapus" class="btn btn-danger btn-sm">
<i class="fa fa-trash"></i> <i class="fa fa-trash"></i>
</a> </>
</td> </td>
</tr> </tr>
@@ -73,26 +65,7 @@
?> ?>
</tbody> </tbody>
</tfoot> </tfoot>
</table> </table>
</div> </div>
</div> </div>
<!-- /.card-body --> <!-- /.card-body -->
<script>
function confirmDelete(event) {
event.preventDefault();
Swal.fire({
title: 'Konfirmasi Hapus',
text: 'Apakah Anda yakin ingin menghapus data ini?',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Ya, Hapus',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = event.currentTarget.href;
}
});
}
</script>

0
admin/lahir/del_lahir.php Executable file → Normal file
View File

0
admin/lahir/edit_lahir.php Executable file → Normal file
View File

0
admin/laporan/laporan_klasifikasi.php Executable file → Normal file
View File

70
admin/mendu/add_mendu.php Executable file → Normal file
View File

@@ -52,41 +52,35 @@
</form> </form>
</div> </div>
<?php <?php
if (isset ($_POST['Simpan'])){ if (isset ($_POST['Simpan'])){
//mulai proses simpan data //mulai proses simpan data
$sql_simpan = "INSERT INTO tb_mendu (id_pdd, tgl_mendu, sebab) VALUES (
// Sanitize Input to prevent SQL Injection & Syntax Errors '".$_POST['id_pdd']."',
$id_pdd = (int)$_POST['id_pdd']; // Cast to integer for safety '".$_POST['tgl_mendu']."',
$tgl_mendu = mysqli_real_escape_string($koneksi, trim($_POST['tgl_mendu'])); '".$_POST['sebab']."')";
$sebab = mysqli_real_escape_string($koneksi, trim($_POST['sebab'])); $query_simpan = mysqli_query($koneksi, $sql_simpan);
$sql_simpan = "INSERT INTO tb_mendu (id_pdd, tgl_mendu, sebab) VALUES ( $sql_ubah = "UPDATE tb_pdd SET
'$id_pdd', status='Meninggal'
'$tgl_mendu', WHERE id_pend='".$_POST['id_pdd']."'";
'$sebab')"; $query_ubah = mysqli_query($koneksi, $sql_ubah);
$query_simpan = mysqli_query($koneksi, $sql_simpan); mysqli_close($koneksi);
$sql_ubah = "UPDATE tb_pdd SET if ($query_simpan && $query_ubah) {
status='Meninggal' echo "<script>
WHERE id_pend='$id_pdd'"; Swal.fire({title: 'Tambah Data Berhasil',text: '',icon: 'success',confirmButtonText: 'OK'
$query_ubah = mysqli_query($koneksi, $sql_ubah); }).then((result) => {if (result.value){
mysqli_close($koneksi); window.location = 'index.php?page=data-mendu';
}
if ($query_simpan && $query_ubah) { })</script>";
echo "<script> }else{
Swal.fire({title: 'Tambah Data Berhasil',text: '',icon: 'success',confirmButtonText: 'OK' echo "<script>
}).then((result) => {if (result.value){ Swal.fire({title: 'Tambah Data Gagal',text: '',icon: 'error',confirmButtonText: 'OK'
window.location = 'index.php?page=data-mendu'; }).then((result) => {if (result.value){
} window.location = 'index.php?page=add-mendu';
})</script>"; }
}else{ })</script>";
echo "<script> }}
Swal.fire({title: 'Tambah Data Gagal',text: '',icon: 'error',confirmButtonText: 'OK' //selesai proses simpan data
}).then((result) => {if (result.value){
window.location = 'index.php?page=add-mendu';
}
})</script>";
}}
//selesai proses simpan data

59
admin/mendu/data_mendu.php Executable file → Normal file
View File

@@ -13,21 +13,20 @@
<br> <br>
<table id="example1" class="table table-bordered table-striped"> <table id="example1" class="table table-bordered table-striped">
<thead> <thead>
<tr> <tr>
<th>No</th> <th>No</th>
<th>NIK</th> <th>NIK</th>
<th>Nama</th> <th>Nama</th>
<th>Jenis Kelamin</th> <th>Tanggal</th>
<th>Tanggal</th> <th>Sebab</th>
<th>Sebab</th> <th>Aksi</th>
<th>Aksi</th> </tr>
</tr>
</thead> </thead>
<tbody> <tbody>
<?php <?php
$no = 1; $no = 1;
$sql = $koneksi->query("SELECT p.id_pend, p.nik, p.nama, p.jekel, m.tgl_mendu, m.sebab, m.id_mendu from $sql = $koneksi->query("SELECT p.id_pend, p.nik, p.nama, m.tgl_mendu, m.sebab, m.id_mendu from
tb_mendu m inner join tb_pdd p on p.id_pend=m.id_pdd"); tb_mendu m inner join tb_pdd p on p.id_pend=m.id_pdd");
while ($data= $sql->fetch_assoc()) { while ($data= $sql->fetch_assoc()) {
?> ?>
@@ -42,12 +41,9 @@
<td> <td>
<?php echo $data['nama']; ?> <?php echo $data['nama']; ?>
</td> </td>
<td> <td>
<?php echo $data['jekel'] == 'LK' ? 'LAKI-LAKI' : 'PEREMPUAN'; ?> <?php echo $data['tgl_mendu']; ?>
</td> </td>
<td>
<?php echo $data['tgl_mendu']; ?>
</td>
<td> <td>
<?php echo $data['sebab']; ?> <?php echo $data['sebab']; ?>
</td> </td>
@@ -60,10 +56,10 @@
class="btn btn-success btn-sm"> class="btn btn-success btn-sm">
<i class="fa fa-edit"></i> <i class="fa fa-edit"></i>
</a> </a>
<a href="?page=del-mendu&kode=<?php echo $data['id_pend']; ?>" onclick="confirmDelete(event)" <a href="?page=del-mendu&kode=<?php echo $data['id_pend']; ?>" onclick="return confirm('Apakah anda yakin hapus data ini ?')"
title="Hapus" class="btn btn-danger btn-sm"> title="Hapus" class="btn btn-danger btn-sm">
<i class="fa fa-trash"></i> <i class="fa fa-trash"></i>
</a> </>
</td> </td>
</tr> </tr>
@@ -72,26 +68,7 @@
?> ?>
</tbody> </tbody>
</tfoot> </tfoot>
</table> </table>
</div> </div>
</div> </div>
<!-- /.card-body --> <!-- /.card-body -->
<script>
function confirmDelete(event) {
event.preventDefault();
Swal.fire({
title: 'Konfirmasi Hapus',
text: 'Apakah Anda yakin ingin menghapus data ini?',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Ya, Hapus',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = event.currentTarget.href;
}
});
}
</script>

0
admin/mendu/del_mendu.php Executable file → Normal file
View File

0
admin/mendu/edit_mendu.php Executable file → Normal file
View File

10
admin/mendu/view_mendu.php Executable file → Normal file
View File

@@ -90,15 +90,7 @@
<b>Jenis Kelamin</b> <b>Jenis Kelamin</b>
</td> </td>
<td>: <td>:
<?php <?php echo $data_cek['jekel']; ?>
if ($data_cek['jekel'] == 'LK') {
echo 'LAKI-LAKI';
} elseif ($data_cek['jekel'] == 'PR') {
echo 'PEREMPUAN';
} else {
echo $data_cek['jekel'];
}
?>
</td> </td>
</tr> </tr>
<tr> <tr>

322
admin/pend/add_pend.php Executable file → Normal file
View File

@@ -1,6 +1,3 @@
<?php
$return_to = isset($_GET['return_to']) ? mysqli_real_escape_string($koneksi, trim($_GET['return_to'])) : '';
?>
<div class="card card-primary"> <div class="card card-primary">
<div class="card-header"> <div class="card-header">
<h3 class="card-title"> <h3 class="card-title">
@@ -91,10 +88,10 @@ $return_to = isset($_GET['return_to']) ? mysqli_real_escape_string($koneksi, tri
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-2 col-form-label">RT/RW</label> <label class="col-sm-2 col-form-label">RT/RW</label>
<div class="col-sm-3"> <div class="col-sm-3">
<input type="text" class="form-control" id="rt" name="rt" placeholder="RT" value="000" required> <input type="text" class="form-control" id="rt" name="rt" placeholder="RT" required>
</div> </div>
<div class="col-sm-3"> <div class="col-sm-3">
<input type="text" class="form-control" id="rw" name="rw" placeholder="RW" value="000" required> <input type="text" class="form-control" id="rw" name="rw" placeholder="RW" required>
</div> </div>
</div> </div>
@@ -130,16 +127,10 @@ $return_to = isset($_GET['return_to']) ? mysqli_real_escape_string($koneksi, tri
<div class="col-sm-6"> <div class="col-sm-6">
<input type="text" class="form-control" id="kewarganegaraan" name="kewarganegaraan" placeholder="WNI/WNA" required> <input type="text" class="form-control" id="kewarganegaraan" name="kewarganegaraan" placeholder="WNI/WNA" required>
</div> </div>
</div> </div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">No. Telepon</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="phone" name="phone" placeholder="Nomor Telepon">
</div>
</div>
</div> </div>
<div class="card-footer"> <div class="card-footer">
<input type="submit" name="Simpan" value="Simpan" class="btn btn-info"> <input type="submit" name="Simpan" value="Simpan" class="btn btn-info">
@@ -149,62 +140,11 @@ $return_to = isset($_GET['return_to']) ? mysqli_real_escape_string($koneksi, tri
</div> </div>
<script> <script>
window.addEventListener('load', function() { window.addEventListener('load', function() {
// Pre-fill form from URL parameters // Scanner Logic
var urlParams = new URLSearchParams(window.location.search); var inputImage = document.getElementById('foto_ktp');
if (urlParams.has('nik')) document.getElementsByName('nik')[0].value = urlParams.get('nik'); var preview = document.getElementById('preview_ktp');
if (urlParams.has('nama')) document.getElementsByName('nama')[0].value = urlParams.get('nama'); var hiddenInput = document.getElementById('foto_cropped');
if (urlParams.has('desa')) document.getElementsByName('desa')[0].value = urlParams.get('desa');
if (urlParams.has('rt')) document.getElementsByName('rt')[0].value = urlParams.get('rt');
if (urlParams.has('rw')) document.getElementsByName('rw')[0].value = urlParams.get('rw');
if (urlParams.has('kecamatan')) document.getElementsByName('kecamatan')[0].value = urlParams.get('kecamatan');
if (urlParams.has('kabupaten')) document.getElementsByName('kabupaten')[0].value = urlParams.get('kabupaten');
if (urlParams.has('provinsi')) document.getElementsByName('provinsi')[0].value = urlParams.get('provinsi');
// NIK Validation (16 digits, numeric)
var nikInput = document.getElementsByName('nik')[0];
function validateNIK(nik) {
nik = nik.trim();
if (nik.length !== 16) return false;
return /^\d+$/.test(nik); // Only digits
}
function showNIKError(message) {
Swal.fire({
icon: 'warning',
title: 'Format NIK Salah',
text: message,
confirmButtonText: 'OK'
});
}
// Validate on blur
if (nikInput) {
nikInput.addEventListener('blur', function() {
var nik = this.value.trim();
if (nik === '') return;
if (!validateNIK(nik)) {
showNIKError('NIK harus terdiri dari 16 digit angka.');
this.focus();
}
});
}
// Validate on form submit
var form = document.querySelector('form');
if (form) {
form.addEventListener('submit', function(e) {
var nik = nikInput.value.trim();
if (nik !== '' && !validateNIK(nik)) {
e.preventDefault();
showNIKError('NIK harus terdiri dari 16 digit angka.');
nikInput.focus();
return false;
}
});
}
// Scanner Logic
var inputImage = document.getElementById('foto_ktp');
var preview = document.getElementById('preview_ktp');
var hiddenInput = document.getElementById('foto_cropped');
// Defines callback for scanner modal // Defines callback for scanner modal
window.handleScannerResult = function(base64) { window.handleScannerResult = function(base64) {
@@ -391,146 +331,102 @@ window.addEventListener('load', function() {
} }
//mulai proses simpan data //mulai proses simpan data
$nik = mysqli_real_escape_string($koneksi, trim($_POST['nik'])); $nik = $_POST['nik'];
$cek_nik = mysqli_query($koneksi, "SELECT * FROM tb_pdd WHERE nik='$nik'"); $cek_nik = mysqli_query($koneksi, "SELECT * FROM tb_pdd WHERE nik='$nik'");
if(mysqli_num_rows($cek_nik) > 0){ if(mysqli_num_rows($cek_nik) > 0){
echo "<script> // AUTO-LINKING: Instead of blocking, we UPDATE the existing record with Scan Data
Swal.fire({title: 'Gagal',text: 'NIK sudah terdaftar dalam sistem!',icon: 'error',confirmButtonText: 'OK' // Sanitize input first before UPDATE
}).then((result) => {if (result.value){ $nama = mysqli_real_escape_string($koneksi, $_POST['nama']);
window.location = 'index.php?page=add-pend" . (!empty($return_to) ? '&return_to=' . $return_to : '') . "'; $tempat_lh = mysqli_real_escape_string($koneksi, $_POST['tempat_lh']);
} $tgl_lh = mysqli_real_escape_string($koneksi, $_POST['tgl_lh']);
})</script>"; $jekel = mysqli_real_escape_string($koneksi, $_POST['jekel']);
return; $desa = mysqli_real_escape_string($koneksi, $_POST['desa']);
} $rt = mysqli_real_escape_string($koneksi, $_POST['rt']);
$rw = mysqli_real_escape_string($koneksi, $_POST['rw']);
// Sanitize Input to prevent SQL Injection & Syntax Errors $agama = mysqli_real_escape_string($koneksi, $_POST['agama']);
$nama = mysqli_real_escape_string($koneksi, trim($_POST['nama'])); $kawin = mysqli_real_escape_string($koneksi, $_POST['kawin']);
$tempat_lh = mysqli_real_escape_string($koneksi, trim($_POST['tempat_lh'])); $pekerjaan = mysqli_real_escape_string($koneksi, $_POST['pekerjaan']);
$tgl_lh = mysqli_real_escape_string($koneksi, trim($_POST['tgl_lh'])); $kewarganegaraan = mysqli_real_escape_string($koneksi, $_POST['kewarganegaraan']);
$jekel = mysqli_real_escape_string($koneksi, trim($_POST['jekel']));
$desa = mysqli_real_escape_string($koneksi, trim($_POST['desa'])); $sql_update = "UPDATE tb_pdd SET
$rt = mysqli_real_escape_string($koneksi, trim($_POST['rt'])); nama='$nama',
$rw = mysqli_real_escape_string($koneksi, trim($_POST['rw'])); tempat_lh='$tempat_lh',
$agama = mysqli_real_escape_string($koneksi, trim($_POST['agama'])); tgl_lh='$tgl_lh',
$kawin = mysqli_real_escape_string($koneksi, trim($_POST['kawin'])); jekel='$jekel',
$pekerjaan = mysqli_real_escape_string($koneksi, trim($_POST['pekerjaan'])); desa='$desa',
$kecamatan = mysqli_real_escape_string($koneksi, trim($_POST['kecamatan'])); rt='$rt',
$kabupaten = mysqli_real_escape_string($koneksi, trim($_POST['kabupaten'])); rw='$rw',
$provinsi = mysqli_real_escape_string($koneksi, trim($_POST['provinsi'])); agama='$agama',
$kewarganegaraan = mysqli_real_escape_string($koneksi, trim($_POST['kewarganegaraan'])); kawin='$kawin',
$phone = mysqli_real_escape_string($koneksi, trim($_POST['phone'])); pekerjaan='$pekerjaan',
kewarganegaraan='$kewarganegaraan',
foto_ktp='$nama_file'
$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, phone) VALUES ( WHERE nik='$nik'";
'$nik',
'$nama', $query_update = mysqli_query($koneksi, $sql_update);
'$tempat_lh',
'$tgl_lh', if ($query_update) {
'$jekel', echo "<script>
'$desa', Swal.fire({title: 'Data Diperbarui',text: 'NIK sudah ada, data penduduk telah diperbarui dengan hasil scan KTP.',icon: 'success',confirmButtonText: 'OK'
'$rt', }).then((result) => {if (result.value){
'$rw', window.location = 'index.php?page=data-pend';
'$agama', }
'$kawin', })</script>";
'$pekerjaan', return;
'$nama_file', }
'Ada', }
'$kecamatan',
'$kabupaten', // Sanitize Input to prevent SQL Injection & Syntax Errors
'$provinsi', $nik = mysqli_real_escape_string($koneksi, $_POST['nik']);
'$kewarganegaraan', '$phone')"; $nama = mysqli_real_escape_string($koneksi, $_POST['nama']);
$query_simpan = mysqli_query($koneksi, $sql_simpan); $tempat_lh = mysqli_real_escape_string($koneksi, $_POST['tempat_lh']);
$tgl_lh = mysqli_real_escape_string($koneksi, $_POST['tgl_lh']);
if ($query_simpan) { $jekel = mysqli_real_escape_string($koneksi, $_POST['jekel']);
$id_pend_baru = mysqli_insert_id($koneksi); $desa = mysqli_real_escape_string($koneksi, $_POST['desa']);
$redirect_url = !empty($return_to) ? "index.php?page=" . $return_to . "&selected_id=" . $id_pend_baru : "index.php?page=data-pend"; $rt = mysqli_real_escape_string($koneksi, $_POST['rt']);
$rw = mysqli_real_escape_string($koneksi, $_POST['rw']);
// KTP → KK: Cari KK yang cocok berdasarkan alamat $agama = mysqli_real_escape_string($koneksi, $_POST['agama']);
$sql_cari_kk = "SELECT k.id_kk, k.no_kk, k.kepala, k.desa, k.rt, k.rw $kawin = mysqli_real_escape_string($koneksi, $_POST['kawin']);
FROM tb_kk k $pekerjaan = mysqli_real_escape_string($koneksi, $_POST['pekerjaan']);
WHERE k.desa='$desa' AND k.rt='$rt' AND k.rw='$rw' $kecamatan = mysqli_real_escape_string($koneksi, $_POST['kecamatan']);
AND k.kec='$kecamatan' AND k.kab='$kabupaten' AND k.prov='$provinsi'"; $kabupaten = mysqli_real_escape_string($koneksi, $_POST['kabupaten']);
$q_cari_kk = mysqli_query($koneksi, $sql_cari_kk); $provinsi = mysqli_real_escape_string($koneksi, $_POST['provinsi']);
$kk_cocok = mysqli_fetch_assoc($q_cari_kk); $kewarganegaraan = mysqli_real_escape_string($koneksi, $_POST['kewarganegaraan']);
if ($kk_cocok) { $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 (
// Tawarkan untuk menghubungkan dengan KK '$nik',
$no_kk = $kk_cocok['no_kk']; '$nama',
$kepala_kk = $kk_cocok['kepala']; '$tempat_lh',
$id_kk = $kk_cocok['id_kk']; '$tgl_lh',
'$jekel',
// Cek apakah sudah terhubung '$desa',
$sql_cek_hubungan = "SELECT * FROM tb_anggota WHERE id_kk='$id_kk' AND id_pend='$id_pend_baru'"; '$rt',
$q_cek_hubungan = mysqli_query($koneksi, $sql_cek_hubungan); '$rw',
'$agama',
if (mysqli_num_rows($q_cek_hubungan) == 0) { '$kawin',
// Simpan sementara data untuk konfirmasi JavaScript '$pekerjaan',
$_SESSION['kk_link_data'] = [ '$nama_file',
'id_pend' => $id_pend_baru, 'Ada',
'id_kk' => $id_kk, '$kecamatan',
'no_kk' => $no_kk, '$kabupaten',
'kepala_kk' => $kepala_kk, '$provinsi',
'nama_pend' => $nama '$kewarganegaraan')";
]; $query_simpan = mysqli_query($koneksi, $sql_simpan);
mysqli_close($koneksi);
echo "<script>
Swal.fire({ if ($query_simpan) {
title: 'Data Penduduk Disimpan!', echo "<script>
html: '<b>Data berhasil disimpan.</b><br><br>Ditemukan KK dengan alamat yang sama:<br><b>No KK: $no_kk</b><br>Kepala Keluarga: $kepala_kk<br><br>Hubungkan ke KK ini sebagai anggota?', Swal.fire({title: 'Tambah Data Berhasil',text: '',icon: 'success',confirmButtonText: 'OK'
icon: 'success', }).then((result) => {if (result.value){
showCancelButton: true, window.location = 'index.php?page=data-pend';
confirmButtonText: 'Ya, Hubungkan', }
cancelButtonText: 'Tidak, Simpan Saja' })</script>";
}).then((result) => { }else{
if (result.value) { echo "<script>
// Kirim permintaan untuk menghubungkan Swal.fire({title: 'Tambah Data Gagal',text: '<?php echo mysqli_error($koneksi); ?>',icon: 'error',confirmButtonText: 'OK'
fetch('admin/pend/link_to_kk.php', { }).then((result) => {if (result.value){
method: 'POST', window.location = 'index.php?page=add-pend';
headers: {'Content-Type': 'application/x-www-form-urlencoded'}, }
body: 'id_pend=$id_pend_baru&id_kk=$id_kk&hubungan=ANGGOTA' })</script>";
}).then(resp => resp.json()).then(data => { }}
if(data.success) {
Swal.fire('Berhasil!', 'Data telah dihubungkan dengan KK.', 'success').then(() => {
window.location = '$redirect_url';
});
} else {
Swal.fire('Gagal', 'Gagal menghubungkan: ' + data.message, 'error').then(() => {
window.location = '$redirect_url';
});
}
});
} else {
window.location = '$redirect_url';
}
});
</script>";
} else {
// Sudah terhubung
echo "<script>
Swal.fire({title: 'Tambah Data Berhasil',text: 'Data telah terhubung dengan KK $no_kk',icon: 'success',confirmButtonText: 'OK'
}).then((result) => {if (result.value){
window.location = '$redirect_url';
}
})</script>";
}
} else {
// Tidak ada KK yang cocok
echo "<script>
Swal.fire({title: 'Tambah Data Berhasil',text: '',icon: 'success',confirmButtonText: 'OK'
}).then((result) => {if (result.value){
window.location = '$redirect_url';
}
})</script>";
}
} else {
$error_redirect = 'index.php?page=add-pend' . (!empty($return_to) ? '&return_to=' . $return_to : '');
echo "<script>
Swal.fire({title: 'Tambah Data Gagal',text: '" . mysqli_error($koneksi) . "',icon: 'error',confirmButtonText: 'OK'
}).then((result) => {if (result.value){
window.location = '$error_redirect';
}
})</script>";
}
mysqli_close($koneksi);
}
//selesai proses simpan data //selesai proses simpan data

12
admin/pend/data_pend.php Executable file → Normal file
View File

@@ -17,7 +17,7 @@
<th>No</th> <th>No</th>
<th>NIK</th> <th>NIK</th>
<th>Nama</th> <th>Nama</th>
<th>Jenis Kelamin</th> <th>JK</th>
<th>Alamat</th> <th>Alamat</th>
<th>No KK</th> <th>No KK</th>
<th>Aksi</th> <th>Aksi</th>
@@ -59,15 +59,7 @@
<?php echo $data['nama']; ?> <?php echo $data['nama']; ?>
</td> </td>
<td> <td>
<?php <?php echo $data['jekel']; ?>
if ($data['jekel'] == 'LK') {
echo 'LAKI-LAKI';
} elseif ($data['jekel'] == 'PR') {
echo 'PEREMPUAN';
} else {
echo $data['jekel'];
}
?>
</td> </td>
<td> <td>
<?php echo $data['desa']; ?> <?php echo $data['desa']; ?>

0
admin/pend/del_pend.php Executable file → Normal file
View File

22
admin/pend/edit_pend.php Executable file → Normal file
View File

@@ -178,16 +178,9 @@
<input type="text" class="form-control" id="kewarganegaraan" name="kewarganegaraan" value="<?php echo $data_cek['kewarganegaraan']; ?>" <input type="text" class="form-control" id="kewarganegaraan" name="kewarganegaraan" value="<?php echo $data_cek['kewarganegaraan']; ?>"
required> required>
</div> </div>
</div> </div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">No. Telepon</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="phone" name="phone" value="<?php echo isset($data_cek['phone']) ? $data_cek['phone'] : ''; ?>" placeholder="Nomor Telepon">
</div>
</div>
</div> </div>
<div class="card-footer"> <div class="card-footer">
<input type="submit" name="Ubah" value="Simpan" class="btn btn-success"> <input type="submit" name="Ubah" value="Simpan" class="btn btn-success">
@@ -389,7 +382,6 @@
$kabupaten = mysqli_real_escape_string($koneksi, $_POST['kabupaten']); $kabupaten = mysqli_real_escape_string($koneksi, $_POST['kabupaten']);
$provinsi = mysqli_real_escape_string($koneksi, $_POST['provinsi']); $provinsi = mysqli_real_escape_string($koneksi, $_POST['provinsi']);
$kewarganegaraan = mysqli_real_escape_string($koneksi, $_POST['kewarganegaraan']); $kewarganegaraan = mysqli_real_escape_string($koneksi, $_POST['kewarganegaraan']);
$phone = mysqli_real_escape_string($koneksi, $_POST['phone']);
if($has_new_photo){ if($has_new_photo){
$foto= $data_cek['foto_ktp']; $foto= $data_cek['foto_ktp'];
@@ -411,9 +403,8 @@
kecamatan='$kecamatan', kecamatan='$kecamatan',
kabupaten='$kabupaten', kabupaten='$kabupaten',
provinsi='$provinsi', provinsi='$provinsi',
kewarganegaraan='$kewarganegaraan', kewarganegaraan='$kewarganegaraan',
phone='$phone', foto_ktp='$nama_file'
foto_ktp='$nama_file'
WHERE id_pend='$id_pend'"; WHERE id_pend='$id_pend'";
$query_ubah = mysqli_query($koneksi, $sql_ubah); $query_ubah = mysqli_query($koneksi, $sql_ubah);
@@ -433,8 +424,7 @@
kecamatan='$kecamatan', kecamatan='$kecamatan',
kabupaten='$kabupaten', kabupaten='$kabupaten',
provinsi='$provinsi', provinsi='$provinsi',
kewarganegaraan='$kewarganegaraan', kewarganegaraan='$kewarganegaraan'
phone='$phone'
WHERE id_pend='$id_pend'"; WHERE id_pend='$id_pend'";
$query_ubah = mysqli_query($koneksi, $sql_ubah); $query_ubah = mysqli_query($koneksi, $sql_ubah);
} }

View File

@@ -1,62 +0,0 @@
<?php
session_start();
include "../inc/koneksi.php";
header('Content-Type: application/json');
if (!isset($_SESSION["ses_username"])) {
echo json_encode(['success' => false, 'message' => 'Unauthorized']);
exit;
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
echo json_encode(['success' => false, 'message' => 'Invalid request method']);
exit;
}
$id_pend = isset($_POST['id_pend']) ? intval($_POST['id_pend']) : 0;
$id_kk = isset($_POST['id_kk']) ? intval($_POST['id_kk']) : 0;
$hubungan = isset($_POST['hubungan']) ? mysqli_real_escape_string($koneksi, $_POST['hubungan']) : 'ANGGOTA';
if ($id_pend <= 0 || $id_kk <= 0) {
echo json_encode(['success' => false, 'message' => 'Invalid ID']);
exit;
}
// Cek apakah sudah terhubung
$sql_cek = "SELECT * FROM tb_anggota WHERE id_kk='$id_kk' AND id_pend='$id_pend'";
$q_cek = mysqli_query($koneksi, $sql_cek);
if (mysqli_num_rows($q_cek) > 0) {
echo json_encode(['success' => false, 'message' => 'Sudah terhubung']);
exit;
}
// Cek apakah penduduk ada
$sql_cek_pend = "SELECT * FROM tb_pdd WHERE id_pend='$id_pend'";
$q_cek_pend = mysqli_query($koneksi, $sql_cek_pend);
if (mysqli_num_rows($q_cek_pend) == 0) {
echo json_encode(['success' => false, 'message' => 'Data penduduk tidak ditemukan']);
exit;
}
// Cek apakah KK ada
$sql_cek_kk = "SELECT * FROM tb_kk WHERE id_kk='$id_kk'";
$q_cek_kk = mysqli_query($koneksi, $sql_cek_kk);
if (mysqli_num_rows($q_cek_kk) == 0) {
echo json_encode(['success' => false, 'message' => 'Data KK tidak ditemukan']);
exit;
}
// Hubungkan
$sql_link = "INSERT INTO tb_anggota (id_kk, id_pend, hubungan) VALUES ('$id_kk', '$id_pend', '$hubungan')";
$q_link = mysqli_query($koneksi, $sql_link);
if ($q_link) {
echo json_encode(['success' => true, 'message' => 'Berhasil dihubungkan']);
} else {
echo json_encode(['success' => false, 'message' => 'Database error: ' . mysqli_error($koneksi)]);
}
mysqli_close($koneksi);
?>

14
admin/pend/view_pend.php Executable file → Normal file
View File

@@ -134,17 +134,9 @@
<td>: <td>:
<?php echo $data_cek['kewarganegaraan']; ?> <?php echo $data_cek['kewarganegaraan']; ?>
</td> </td>
</tr> </tr>
<tr>
<td style="width: 150px">
<b>No. Telepon</b>
</td>
<td>:
<?php echo isset($data_cek['phone']) ? $data_cek['phone'] : ''; ?>
</td>
</tr>
</tbody> </tbody>
</table> </table>
<div class="card-footer"> <div class="card-footer">

80
admin/pengguna/add_pengguna.php Executable file → Normal file
View File

@@ -30,11 +30,11 @@
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-2 col-form-label">Level</label> <label class="col-sm-2 col-form-label">Level</label>
<div class="col-sm-4"> <div class="col-sm-4">
<select name="level" id="level" class="form-control"> <select name="level" id="level" class="form-control">
<option value="">- Pilih -</option> <option>- Pilih -</option>
<option value="Administrator">Administrator</option> <option>Administrator</option>
<option value="Kaur Pemerintah">Kaur Pemerintah</option> <option>Kaur Pemerintah</option>
</select> </select>
</div> </div>
</div> </div>
@@ -46,45 +46,31 @@
</form> </form>
</div> </div>
<?php <?php
if (isset ($_POST['Simpan'])){ if (isset ($_POST['Simpan'])){
// Map level untuk database (enum: 'admin', 'kaur') //mulai proses simpan data
$level_map = [ $sql_simpan = "INSERT INTO tb_pengguna (nama_pengguna,username,password,level) VALUES (
'Administrator' => 'admin', '".$_POST['nama_pengguna']."',
'Kaur Pemerintah' => 'kaur' '".$_POST['username']."',
]; '".$_POST['password']."',
$level_db = isset($level_map[$_POST['level']]) ? $level_map[$_POST['level']] : $_POST['level']; '".$_POST['level']."')";
$query_simpan = mysqli_query($koneksi, $sql_simpan);
// Sanitize Input to prevent SQL Injection & Syntax Errors mysqli_close($koneksi);
$nama_pengguna = mysqli_real_escape_string($koneksi, trim($_POST['nama_pengguna']));
$username = mysqli_real_escape_string($koneksi, trim($_POST['username'])); if ($query_simpan) {
$password_raw = trim($_POST['password']); echo "<script>
$password_hash = MD5($password_raw); Swal.fire({title: 'Tambah Data Berhasil',text: '',icon: 'success',confirmButtonText: 'OK'
$level_db = mysqli_real_escape_string($koneksi, $level_db); }).then((result) => {if (result.value){
window.location = 'index.php?page=data-pengguna';
//mulai proses simpan data }
$sql_simpan = "INSERT INTO tb_pengguna (nama_pengguna,username,password,level) VALUES ( })</script>";
'$nama_pengguna', }else{
'$username', echo "<script>
'$password_hash', Swal.fire({title: 'Tambah Data Gagal',text: '',icon: 'error',confirmButtonText: 'OK'
'$level_db')"; }).then((result) => {if (result.value){
$query_simpan = mysqli_query($koneksi, $sql_simpan); window.location = 'index.php?page=add-pengguna';
mysqli_close($koneksi); }
})</script>";
if ($query_simpan) { }}
echo "<script> //selesai proses simpan data
Swal.fire({title: 'Tambah Data Berhasil',text: '',icon: 'success',confirmButtonText: 'OK'
}).then((result) => {if (result.value){
window.location = 'index.php?page=data-pengguna';
}
})</script>";
}else{
echo "<script>
Swal.fire({title: 'Tambah Data Gagal',text: '',icon: 'error',confirmButtonText: 'OK'
}).then((result) => {if (result.value){
window.location = 'index.php?page=add-pengguna';
}
})</script>";
}}
//selesai proses simpan data

48
admin/pengguna/data_pengguna.php Executable file → Normal file
View File

@@ -23,16 +23,11 @@
</thead> </thead>
<tbody> <tbody>
<?php <?php
$no = 1; $no = 1;
$level_map = [ $sql = $koneksi->query("select * from tb_pengguna");
'admin' => 'Administrator', while ($data= $sql->fetch_assoc()) {
'kaur' => 'Kaur Pemerintah' ?>
];
$sql = $koneksi->query("select * from tb_pengguna");
while ($data= $sql->fetch_assoc()) {
$level_display = isset($level_map[$data['level']]) ? $level_map[$data['level']] : $data['level'];
?>
<tr> <tr>
<td> <td>
@@ -45,17 +40,17 @@
<?php echo $data['username']; ?> <?php echo $data['username']; ?>
</td> </td>
<td> <td>
<?php echo $level_display; ?> <?php echo $data['level']; ?>
</td> </td>
<td> <td>
<a href="?page=edit-pengguna&kode=<?php echo $data['id_pengguna']; ?>" title="Ubah" <a href="?page=edit-pengguna&kode=<?php echo $data['id_pengguna']; ?>" title="Ubah"
class="btn btn-success btn-sm"> class="btn btn-success btn-sm">
<i class="fa fa-edit"></i> <i class="fa fa-edit"></i>
</a> </a>
<a href="?page=del-pengguna&kode=<?php echo $data['id_pengguna']; ?>" onclick="confirmDelete(event)" <a href="?page=del-pengguna&kode=<?php echo $data['id_pengguna']; ?>" onclick="return confirm('Apakah anda yakin hapus data ini ?')"
title="Hapus" class="btn btn-danger btn-sm"> title="Hapus" class="btn btn-danger btn-sm">
<i class="fa fa-trash"></i> <i class="fa fa-trash"></i>
</a> </>
</td> </td>
</tr> </tr>
@@ -64,26 +59,7 @@
?> ?>
</tbody> </tbody>
</tfoot> </tfoot>
</table> </table>
</div> </div>
</div> </div>
<!-- /.card-body --> <!-- /.card-body -->
<script>
function confirmDelete(event) {
event.preventDefault();
Swal.fire({
title: 'Konfirmasi Hapus',
text: 'Apakah Anda yakin ingin menghapus data ini?',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Ya, Hapus',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = event.currentTarget.href;
}
});
}
</script>

0
admin/pengguna/del_pengguna.php Executable file → Normal file
View File

57
admin/pengguna/edit_pengguna.php Executable file → Normal file
View File

@@ -46,29 +46,17 @@
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-2 col-form-label">Level</label> <label class="col-sm-2 col-form-label">Level</label>
<div class="col-sm-4"> <div class="col-sm-4">
<select name="level" id="level" class="form-control"> <select name="level" id="level" class="form-control">
<option value="">-- Pilih Level --</option> <option value="">-- Pilih Level --</option>
<?php <?php
// Mapping level dari database ke tampilan //menhecek data yg dipilih sebelumnya
$level_display_map = [ if ($data_cek['level'] == "Administrator") echo "<option value='Administrator' selected>Administrator</option>";
'admin' => 'Administrator', else echo "<option value='Administrator'>Administrator</option>";
'kaur' => 'Kaur Pemerintah',
'Administrator' => 'Administrator', // backward compatibility if ($data_cek['level'] == "Kaur Pemerintah") echo "<option value='Kaur Pemerintah' selected>Kaur Pemerintah</option>";
'Kaur Pemerintah' => 'Kaur Pemerintah' else echo "<option value='Kaur Pemerintah'>Kaur Pemerintah</option>";
]; ?>
$current_level = $data_cek['level']; </select>
$current_display = isset($level_display_map[$current_level]) ? $level_display_map[$current_level] : $current_level;
// Opsi Administrator
if ($current_display == "Administrator") {
echo "<option value='Administrator' selected>Administrator</option>";
echo "<option value='Kaur Pemerintah'>Kaur Pemerintah</option>";
} else {
echo "<option value='Administrator'>Administrator</option>";
echo "<option value='Kaur Pemerintah' selected>Kaur Pemerintah</option>";
}
?>
</select>
</div> </div>
</div> </div>
@@ -84,21 +72,14 @@
<?php <?php
if (isset ($_POST['Ubah'])){ if (isset ($_POST['Ubah'])){
// Map level untuk database (enum: 'admin', 'kaur') $sql_ubah = "UPDATE tb_pengguna SET
$level_map = [ nama_pengguna='".$_POST['nama_pengguna']."',
'Administrator' => 'admin', username='".$_POST['username']."',
'Kaur Pemerintah' => 'kaur' password='".$_POST['password']."',
]; level='".$_POST['level']."'
$level_db = isset($level_map[$_POST['level']]) ? $level_map[$_POST['level']] : $_POST['level']; WHERE id_pengguna='".$_POST['id_pengguna']."'";
$query_ubah = mysqli_query($koneksi, $sql_ubah);
$sql_ubah = "UPDATE tb_pengguna SET
nama_pengguna='".$_POST['nama_pengguna']."',
username='".$_POST['username']."',
password='".$_POST['password']."',
level='".$level_db."'
WHERE id_pengguna='".$_POST['id_pengguna']."'";
$query_ubah = mysqli_query($koneksi, $sql_ubah);
mysqli_close($koneksi); mysqli_close($koneksi);
if ($query_ubah) { if ($query_ubah) {

70
admin/pindah/add_pindah.php Executable file → Normal file
View File

@@ -52,41 +52,35 @@
</form> </form>
</div> </div>
<?php <?php
if (isset ($_POST['Simpan'])){ if (isset ($_POST['Simpan'])){
//mulai proses simpan data //mulai proses simpan data
$sql_simpan = "INSERT INTO tb_pindah (id_pdd, tgl_pindah, alasan) VALUES (
// Sanitize Input to prevent SQL Injection & Syntax Errors '".$_POST['id_pdd']."',
$id_pdd = (int)$_POST['id_pdd']; // Cast to integer for safety '".$_POST['tgl_pindah']."',
$tgl_pindah = mysqli_real_escape_string($koneksi, trim($_POST['tgl_pindah'])); '".$_POST['alasan']."')";
$alasan = mysqli_real_escape_string($koneksi, trim($_POST['alasan'])); $query_simpan = mysqli_query($koneksi, $sql_simpan);
$sql_simpan = "INSERT INTO tb_pindah (id_pdd, tgl_pindah, alasan) VALUES ( $sql_ubah = "UPDATE tb_pdd SET
'$id_pdd', status='Pindah'
'$tgl_pindah', WHERE id_pend='".$_POST['id_pdd']."'";
'$alasan')"; $query_ubah = mysqli_query($koneksi, $sql_ubah);
$query_simpan = mysqli_query($koneksi, $sql_simpan); mysqli_close($koneksi);
$sql_ubah = "UPDATE tb_pdd SET if ($query_simpan && $query_ubah) {
status='Pindah' echo "<script>
WHERE id_pend='$id_pdd'"; Swal.fire({title: 'Tambah Data Berhasil',text: '',icon: 'success',confirmButtonText: 'OK'
$query_ubah = mysqli_query($koneksi, $sql_ubah); }).then((result) => {if (result.value){
mysqli_close($koneksi); window.location = 'index.php?page=data-pindah';
}
if ($query_simpan && $query_ubah) { })</script>";
echo "<script> }else{
Swal.fire({title: 'Tambah Data Berhasil',text: '',icon: 'success',confirmButtonText: 'OK' echo "<script>
}).then((result) => {if (result.value){ Swal.fire({title: 'Tambah Data Gagal',text: '',icon: 'error',confirmButtonText: 'OK'
window.location = 'index.php?page=data-pindah'; }).then((result) => {if (result.value){
} window.location = 'index.php?page=add-pindah';
})</script>"; }
}else{ })</script>";
echo "<script> }}
Swal.fire({title: 'Tambah Data Gagal',text: '',icon: 'error',confirmButtonText: 'OK' //selesai proses simpan data
}).then((result) => {if (result.value){
window.location = 'index.php?page=add-pindah';
}
})</script>";
}}
//selesai proses simpan data

59
admin/pindah/data_pindah.php Executable file → Normal file
View File

@@ -13,21 +13,20 @@
<br> <br>
<table id="example1" class="table table-bordered table-striped"> <table id="example1" class="table table-bordered table-striped">
<thead> <thead>
<tr> <tr>
<th>No</th> <th>No</th>
<th>NIK</th> <th>NIK</th>
<th>Nama</th> <th>Nama</th>
<th>Jenis Kelamin</th> <th>Tanggal</th>
<th>Tanggal</th> <th>Alasan</th>
<th>Alasan</th> <th>Aksi</th>
<th>Aksi</th> </tr>
</tr>
</thead> </thead>
<tbody> <tbody>
<?php <?php
$no = 1; $no = 1;
$sql = $koneksi->query("SELECT p.id_pend, p.nik, p.nama, p.jekel, d.tgl_pindah, d.alasan, d.id_pindah from $sql = $koneksi->query("SELECT p.id_pend, p.nik, p.nama, d.tgl_pindah, d.alasan, d.id_pindah from
tb_pindah d inner join tb_pdd p on p.id_pend=d.id_pdd"); tb_pindah d inner join tb_pdd p on p.id_pend=d.id_pdd");
while ($data= $sql->fetch_assoc()) { while ($data= $sql->fetch_assoc()) {
?> ?>
@@ -42,12 +41,9 @@
<td> <td>
<?php echo $data['nama']; ?> <?php echo $data['nama']; ?>
</td> </td>
<td> <td>
<?php echo $data['jekel'] == 'LK' ? 'LAKI-LAKI' : 'PEREMPUAN'; ?> <?php echo $data['tgl_pindah']; ?>
</td> </td>
<td>
<?php echo $data['tgl_pindah']; ?>
</td>
<td> <td>
<?php echo $data['alasan']; ?> <?php echo $data['alasan']; ?>
</td> </td>
@@ -60,10 +56,10 @@
class="btn btn-success btn-sm"> class="btn btn-success btn-sm">
<i class="fa fa-edit"></i> <i class="fa fa-edit"></i>
</a> </a>
<a href="?page=del-pindah&kode=<?php echo $data['id_pend']; ?>" onclick="confirmDelete(event)" <a href="?page=del-pindah&kode=<?php echo $data['id_pend']; ?>" onclick="return confirm('Apakah anda yakin hapus data ini ?')"
title="Hapus" class="btn btn-danger btn-sm"> title="Hapus" class="btn btn-danger btn-sm">
<i class="fa fa-trash"></i> <i class="fa fa-trash"></i>
</a> </>
</td> </td>
</tr> </tr>
@@ -72,26 +68,7 @@
?> ?>
</tbody> </tbody>
</tfoot> </tfoot>
</table> </table>
</div> </div>
</div> </div>
<!-- /.card-body --> <!-- /.card-body -->
<script>
function confirmDelete(event) {
event.preventDefault();
Swal.fire({
title: 'Konfirmasi Hapus',
text: 'Apakah Anda yakin ingin menghapus data ini?',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Ya, Hapus',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = event.currentTarget.href;
}
});
}
</script>

0
admin/pindah/del_pindah.php Executable file → Normal file
View File

0
admin/pindah/edit_pindah.php Executable file → Normal file
View File

10
admin/pindah/view_pindah.php Executable file → Normal file
View File

@@ -90,15 +90,7 @@
<b>Jenis Kelamin</b> <b>Jenis Kelamin</b>
</td> </td>
<td>: <td>:
<?php <?php echo $data_cek['jekel']; ?>
if ($data_cek['jekel'] == 'LK') {
echo 'LAKI-LAKI';
} elseif ($data_cek['jekel'] == 'PR') {
echo 'PEREMPUAN';
} else {
echo $data_cek['jekel'];
}
?>
</td> </td>
</tr> </tr>
<tr> <tr>

0
admin/profil/data_profil.php Executable file → Normal file
View File

888
admin/scanner_modal.php Executable file → Normal file
View File

@@ -3,33 +3,29 @@
<div class="modal fade" id="modalScanner" tabindex="-1" role="dialog" aria-labelledby="modalScannerLabel" aria-hidden="true" data-backdrop="static"> <div class="modal fade" id="modalScanner" tabindex="-1" role="dialog" aria-labelledby="modalScannerLabel" aria-hidden="true" data-backdrop="static">
<div class="modal-dialog modal-xl" role="document"> <div class="modal-dialog modal-xl" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="modalScannerLabel"><i class="fas fa-expand"></i> Smart Scanner</h5> <h5 class="modal-title" id="modalScannerLabel"><i class="fas fa-expand"></i> Smart Scanner</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="modal-body text-center bg-dark p-0" style="position: relative; overflow: hidden; height: 80vh;"> <div class="modal-body text-center bg-dark p-0" style="position: relative; overflow: hidden; height: 80vh;">
<div id="scanner-container" style="position: relative; margin: auto; display: inline-block;"> <!-- Container for Canvases -->
<canvas id="canvas-image" style="position: absolute; left: 0; top: 0; z-index: 1;"></canvas> <!-- Container for Canvases -->
<canvas id="canvas-overlay" style="position: absolute; left: 0; top: 0; z-index: 2; cursor: crosshair;"></canvas> <div id="scanner-container" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); box-shadow: 0 0 10px rgba(0,0,0,0.5);">
</div> <canvas id="canvas-image" style="position: absolute; left: 0; top: 0; z-index: 10;"></canvas>
<canvas id="canvas-overlay" style="position: absolute; left: 0; top: 0; z-index: 20; cursor: crosshair;"></canvas>
<div id="scanner-loading" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); color: white; display: none;"> </div>
<i class="fas fa-spinner fa-spin fa-3x"></i><br>Detecting Document... <div id="scanner-loading" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); color: white; display: none; z-index: 50; text-align: center;">
</div> <i class="fas fa-spinner fa-spin fa-3x"></i><br><span style="margin-top: 10px; display: block;">Sedang menganalisa...</span>
</div>
<!-- Mobile Help Tips -->
<div id="mobile-help" class="d-none d-md-none d-lg-none" style="position: absolute; bottom: 10px; left: 0; right: 0; text-align: center; color: white; background: rgba(0,0,0,0.7); padding: 5px; font-size: 12px;">
<span>📍 Sentuh & geser titik biru untuk atur sudut dokumen</span>
</div>
</div> </div>
<div class="modal-footer justify-content-between"> <div class="modal-footer justify-content-between">
<div> <div>
<button type="button" class="btn btn-secondary" id="btnScanRotateLeft" title="Putar Kiri (-90°)"><i class="fas fa-undo"></i></button> <button type="button" class="btn btn-secondary" id="btnScanRotateLeft" title="Putar Kiri (-90°)"><i class="fas fa-undo"></i></button>
<button type="button" class="btn btn-secondary" id="btnScanRotateRight" title="Putar Kanan (+90°)"><i class="fas fa-redo"></i></button> <button type="button" class="btn btn-secondary" id="btnScanRotateRight" title="Putar Kanan (+90°)"><i class="fas fa-redo"></i></button>
<button type="button" class="btn btn-warning" id="btnScanReset"><i class="fas fa-sync"></i> Reset Sudut</button> <button type="button" class="btn btn-warning" id="btnScanReset"><i class="fas fa-sync"></i> Reset Sudut</button>
</div> </div>
<div> <div>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button> <button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
<button type="button" class="btn btn-primary" id="btnScanSave"><i class="fas fa-check"></i> Simpan Hasil Scan</button> <button type="button" class="btn btn-primary" id="btnScanSave"><i class="fas fa-check"></i> Simpan Hasil Scan</button>
@@ -39,341 +35,31 @@
</div> </div>
</div> </div>
<style>
/* Disable all transitions and hover effects for scanner modal */
#modalScanner .card,
#modalScanner .modal-content,
#modalScanner .modal-body,
#modalScanner .modal-header,
#modalScanner .modal-footer,
#modalScanner .modal-dialog,
#modalScanner #scanner-container,
#modalScanner #scanner-container * {
transition: none !important;
}
#modalScanner .card:hover,
#modalScanner .modal-content:hover,
#modalScanner .modal-body:hover,
#modalScanner .modal-header:hover,
#modalScanner .modal-footer:hover,
#modalScanner .modal-dialog:hover,
#modalScanner #scanner-container:hover,
#modalScanner #scanner-container *:hover {
transform: none !important;
}
/* Ensure canvas is fully visible */
#canvas-image {
opacity: 1 !important;
filter: none !important;
background-color: white !important;
}
#canvas-overlay {
opacity: 1 !important;
filter: none !important;
}
/* Prevent canvas from moving on hover */
#canvas-image:hover,
#canvas-overlay:hover {
transform: none !important;
}
/* Prevent modal dragging and improve touch handling on mobile */
#modalScanner .modal-dialog,
#modalScanner .modal-content,
#modalScanner .modal-header,
#modalScanner .modal-footer {
touch-action: none !important; /* Prevent browser touch gestures (pan, zoom, swipe) */
user-select: none !important; /* Prevent text selection during drag */
-webkit-user-select: none !important;
-webkit-touch-callout: none !important;
}
/* Allow touch interaction only on canvas and buttons */
#modalScanner .modal-body,
#canvas-image,
#canvas-overlay {
touch-action: manipulation !important; /* Allow pinch-zoom and pan on canvas only */
}
/* Prevent modal backdrop from responding to touch */
.modal-backdrop {
touch-action: none !important;
}
/* Lock modal position on mobile */
@media (max-width: 768px) {
#modalScanner {
padding-right: 0 !important; /* Prevent shift from scrollbar */
}
#modalScanner .modal-dialog {
margin: 0 !important;
max-height: 100vh !important;
height: 100vh !important;
width: 100vw !important;
max-width: 100vw !important;
position: fixed !important;
top: 0 !important;
left: 0 !important;
right: 0 !important;
bottom: 0 !important;
transform: none !important;
transition: none !important;
}
#modalScanner .modal-content {
border-radius: 0 !important;
height: 100vh !important;
max-height: 100vh !important;
width: 100vw !important;
max-width: 100vw !important;
overflow: hidden !important;
position: fixed !important;
top: 0 !important;
left: 0 !important;
transform: none !important;
transition: none !important;
}
#modalScanner .modal-body {
height: calc(100vh - 120px) !important; /* Account for header and footer */
overflow: hidden !important;
}
/* Prevent any modal movement */
.modal-open #modalScanner {
overflow: hidden !important;
}
/* Larger touch target for close button on mobile */
#modalScanner .modal-header .close {
padding: 20px !important;
font-size: 2rem !important;
line-height: 1 !important;
margin: -10px -10px -10px auto !important;
}
/* Larger touch targets for all buttons */
#modalScanner .modal-footer .btn {
min-height: 44px !important;
min-width: 44px !important;
padding: 10px 15px !important;
font-size: 16px !important; /* Prevent zoom on iOS */
}
/* Larger corner points for touch */
#canvas-overlay {
touch-action: manipulation;
}
/* Help text */
#mobile-help {
font-size: 14px !important;
padding: 10px !important;
}
}
/* Larger hit area for corner points for touch devices */
#canvas-overlay {
touch-action: pinch-zoom;
}
</style>
<script> <script>
window.addEventListener('load', function() { window.addEventListener('load', function() {
// Function to load scanner libraries dynamically
function loadScannerLibs() {
return new Promise(function(resolve, reject) {
// Check if both libraries are already loaded AND OpenCV runtime is ready
if (typeof cv !== 'undefined' && cv.Mat && typeof jscanify !== 'undefined') {
resolve();
return;
}
// Load OpenCV first (jscanify depends on it)
function loadOpenCV() {
return new Promise(function(resolveOpenCV, rejectOpenCV) {
if (typeof cv !== 'undefined') {
// OpenCV already loaded, check if runtime ready
if (cv.Mat) {
resolveOpenCV();
} else if (cv.onRuntimeInitialized) {
// Wait for runtime initialization
var existingCallback = cv.onRuntimeInitialized;
cv.onRuntimeInitialized = function() {
if (typeof existingCallback === 'function') {
existingCallback();
}
resolveOpenCV();
};
} else {
// OpenCV loaded but no runtime callback mechanism
// Poll for cv.Mat
var checkInterval = setInterval(function() {
if (cv.Mat) {
clearInterval(checkInterval);
resolveOpenCV();
}
}, 100);
// Timeout after 10 seconds
setTimeout(function() {
clearInterval(checkInterval);
rejectOpenCV(new Error('OpenCV runtime initialization timeout'));
}, 10000);
}
} else {
// Load OpenCV script
var opencvScript = document.createElement('script');
opencvScript.src = '../../plugins/vendor/opencv/opencv.js';
opencvScript.onload = function() {
console.log('OpenCV.js loaded dynamically');
// Wait for runtime initialization
if (cv.onRuntimeInitialized) {
var existingCallback = cv.onRuntimeInitialized;
cv.onRuntimeInitialized = function() {
if (typeof existingCallback === 'function') {
existingCallback();
}
resolveOpenCV();
};
} else {
// Fallback: wait a bit then resolve
setTimeout(resolveOpenCV, 5000);
}
};
opencvScript.onerror = function() {
console.error('Failed to load OpenCV.js');
rejectOpenCV(new Error('Failed to load OpenCV.js'));
};
document.head.appendChild(opencvScript);
}
});
}
// Load jscanify after OpenCV is ready
function loadJscanify() {
return new Promise(function(resolveJscanify, rejectJscanify) {
if (typeof jscanify !== 'undefined') {
resolveJscanify();
return;
}
var jscanifyScript = document.createElement('script');
jscanifyScript.src = '../../plugins/vendor/jscanify/jscanify.min.js';
jscanifyScript.onload = function() {
console.log('jscanify loaded dynamically');
resolveJscanify();
};
jscanifyScript.onerror = function() {
console.error('Failed to load jscanify');
rejectJscanify(new Error('Failed to load jscanify'));
};
document.head.appendChild(jscanifyScript);
});
}
// Load all libraries in sequence
loadOpenCV()
.then(function() {
console.log('OpenCV ready, loading jscanify...');
return loadJscanify();
})
.then(function() {
console.log('All scanner libraries loaded');
resolve();
})
.catch(function(error) {
console.error('Failed to load scanner libraries:', error);
reject(error);
});
});
}
// Load libraries and then initialize scanner
loadScannerLibs().then(function() {
// Give OpenCV a moment to initialize runtime
setTimeout(function() {
// Scanner Variables // Scanner Variables
var scannerModal = $('#modalScanner'); var scannerModal = $('#modalScanner');
var canvasImage = document.getElementById('canvas-image'); var canvasImage = document.getElementById('canvas-image');
var canvasOverlay = document.getElementById('canvas-overlay'); var canvasOverlay = document.getElementById('canvas-overlay');
var ctxImg = canvasImage.getContext('2d'); var ctxImg = canvasImage.getContext('2d');
var ctxOver = canvasOverlay.getContext('2d'); var ctxOver = canvasOverlay.getContext('2d');
const IS_TOUCH_DEVICE = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
var scanner = null; var scanner = new jscanify();
var originalImg = new Image();
function initScanner() {
// Check if OpenCV is loaded
if (typeof cv === 'undefined') {
console.warn('OpenCV not loaded yet');
return false;
}
// Check if OpenCV runtime is already initialized
if (cv.Mat) {
try {
scanner = new jscanify();
console.log('Scanner initialized successfully (runtime ready)');
return true;
} catch (e) {
console.error('Failed to initialize jscanify:', e);
return false;
}
}
// Runtime not initialized yet, set up callback
if (cv.onRuntimeInitialized) {
// Save any existing callback
var existingCallback = cv.onRuntimeInitialized;
cv.onRuntimeInitialized = function() {
// Call existing callback first if it's a function
if (typeof existingCallback === 'function') {
existingCallback();
}
// Now initialize scanner
try {
scanner = new jscanify();
console.log('Scanner initialized after runtime ready (callback)');
} catch (e) {
console.error('Failed to initialize jscanify after runtime:', e);
}
};
console.log('Waiting for OpenCV runtime initialization...');
return false;
}
// Should not reach here
console.warn('OpenCV loaded but no runtime initialization mechanism');
return false;
}
// Try to initialize scanner immediately
initScanner();
var originalImg = new Image();
// Mode is always smart scan now
var currentMode = 'smart';
// Corner Points (tl, tr, bl, br) // Corner Points (tl, tr, bl, br)
var corners = []; var corners = [];
var activePoint = null; var activePoint = null;
var isDragging = false; var isDragging = false;
var touchStartPos = null;
var isTouchInteraction = false;
var touchOffset = null;
// Config - Extra large circles for easy mobile touch, thin lines // Config
const POINT_RADIUS = 50; // Extra large circle radius for easy mobile touch const POINT_RADIUS = 15;
const POINT_COLOR = 'rgba(0, 123, 255, 0.15)'; // Transparent blue (15% opacity) const POINT_COLOR = '#007bff';
const POINT_OUTLINE_COLOR = '#007bff'; // Blue outline const LINE_COLOR = '#00ff00';
const POINT_OUTLINE_WIDTH = 2; // Thin outline const LINE_WIDTH = 3;
const LINE_COLOR = 'rgba(0, 255, 0, 0.8)'; // Semi-transparent green lines
const LINE_WIDTH = 1.5; // Very thin lines
const TOUCH_RADIUS_MULTIPLIER = 3.5; // Extra large hit area for touch devices
const TOUCH_SLOP = 5; // pixels threshold before dragging starts
// Mode is always smart scan (no toggle needed) // --- Public Function to Open Scanner ---
window.openScanner = function(file) {
// --- Public Function to Open Scanner ---
window.openScanner = function(file) {
if (!file) return; if (!file) return;
// Reset State // Reset State
@@ -390,7 +76,10 @@ window.addEventListener('load', function() {
function checkReady() { function checkReady() {
if (imgLoaded && modalShown) { if (imgLoaded && modalShown) {
setupScannerCanvas(); // Defer execution to allow UI (spinner) to render first
setTimeout(function() {
initScanner();
}, 100);
} }
} }
@@ -405,151 +94,77 @@ window.addEventListener('load', function() {
}; };
reader.readAsDataURL(file); reader.readAsDataURL(file);
// 2. Show Modal & Listen // 2. Show Modal & Listen
scannerModal.off('shown.bs.modal'); // Remove old listeners scannerModal.off('shown.bs.modal'); // Remove old listeners
scannerModal.on('shown.bs.modal', function() { scannerModal.on('shown.bs.modal', function() {
modalShown = true; modalShown = true;
checkReady(); checkReady();
});
// Prevent body scrolling on mobile
document.body.style.overflow = 'hidden';
document.body.style.position = 'fixed';
document.body.style.width = '100%';
// Prevent modal dragging on mobile
var modalDialog = document.querySelector('#modalScanner .modal-dialog');
var modalContent = document.querySelector('#modalScanner .modal-content');
var modalHeader = document.querySelector('#modalScanner .modal-header');
var modalFooter = document.querySelector('#modalScanner .modal-footer');
var preventTouch = function(e) {
// Allow touch on canvas elements and buttons
var target = e.target;
var isCanvas = target.id === 'canvas-overlay' || target.id === 'canvas-image' ||
target.closest('#scanner-container');
var isButton = target.tagName === 'BUTTON' || target.closest('button');
if (!isCanvas && !isButton) {
e.preventDefault();
e.stopPropagation();
}
};
if (modalDialog) {
modalDialog.addEventListener('touchstart', preventTouch, { passive: false });
modalDialog.addEventListener('touchmove', preventTouch, { passive: false });
}
if (modalContent) {
modalContent.addEventListener('touchstart', preventTouch, { passive: false });
modalContent.addEventListener('touchmove', preventTouch, { passive: false });
}
if (modalHeader) {
modalHeader.addEventListener('touchstart', preventTouch, { passive: false });
modalHeader.addEventListener('touchmove', preventTouch, { passive: false });
}
if (modalFooter) {
modalFooter.addEventListener('touchstart', preventTouch, { passive: false });
modalFooter.addEventListener('touchmove', preventTouch, { passive: false });
}
});
// Restore scrolling when modal is hidden
scannerModal.on('hidden.bs.modal', function() {
document.body.style.overflow = '';
document.body.style.position = '';
document.body.style.width = '';
});
scannerModal.modal('show'); scannerModal.modal('show');
}; };
function detectDocument(scale, w, h, showAlert = false) { function initScanner() {
// Detect Contour using jscanify // Resize Canvas to fit screen but keep aspect ratio
try { var maxWidth = $('#modalScanner .modal-body').width() - 20;
// jscanify expects an image element, we can pass originalImg but we need to map coordinates var maxHeight = $('#modalScanner .modal-body').height() - 20;
// Wait, jscanify uses OpenCV which might not be ready.
if (typeof cv !== 'undefined' && cv.Mat) { var scale = Math.min(maxWidth / originalImg.width, maxHeight / originalImg.height);
// We need to work on the original image for detection, then scale points var w = originalImg.width * scale;
var contour = scanner ? scanner.findPaper(originalImg) : null; var h = originalImg.height * scale;
// contour returns { topLeftCorner, topRightCorner, bottomLeftCorner, bottomRightCorner } each {x, y}
canvasImage.width = w;
if (contour) { canvasImage.height = h;
// Order: TL, TR, BR, BL (clockwise) canvasOverlay.width = w;
corners = [ canvasOverlay.height = h;
{ x: contour.topLeftCorner.x * scale, y: contour.topLeftCorner.y * scale },
{ x: contour.topRightCorner.x * scale, y: contour.topRightCorner.y * scale }, // Resize container
{ x: contour.bottomRightCorner.x * scale, y: contour.bottomRightCorner.y * scale }, $('#scanner-container').css({ width: w, height: h });
{ x: contour.bottomLeftCorner.x * scale, y: contour.bottomLeftCorner.y * scale }
]; // Draw Image
ctxImg.drawImage(originalImg, 0, 0, w, h);
// Detect Contour using jscanify
try {
// jscanify expects an image element, we can pass originalImg but we need to map coordinates
// Wait, jscanify uses OpenCV which might not be ready.
if (typeof cv !== 'undefined' && cv.Mat) {
// We need to work on the original image for detection, then scale points
var contour = scanner.findPaper(originalImg);
// contour returns { topLeftCorner, topRightCorner, bottomLeftCorner, bottomRightCorner } each {x, y}
if (contour) {
corners = [
{ x: contour.topLeftCorner.x * scale, y: contour.topLeftCorner.y * scale },
{ x: contour.topRightCorner.x * scale, y: contour.topRightCorner.y * scale },
{ x: contour.bottomRightCorner.x * scale, y: contour.bottomRightCorner.y * scale }, // Order: tr -> br -> bl ?? No, usually tl, tr, br, bl order for polygon drawing
{ x: contour.bottomLeftCorner.x * scale, y: contour.bottomLeftCorner.y * scale }
];
// Reorder primarily for logic: TL, TR, BR, BL
corners = [
{ x: contour.topLeftCorner.x * scale, y: contour.topLeftCorner.y * scale },
{ x: contour.topRightCorner.x * scale, y: contour.topRightCorner.y * scale },
{ x: contour.bottomRightCorner.x * scale, y: contour.bottomRightCorner.y * scale },
{ x: contour.bottomLeftCorner.x * scale, y: contour.bottomLeftCorner.y * scale }
];
} else { } else {
defaultCorners(w, h); defaultCorners(w, h);
if (showAlert) { }
Swal.fire({ } else {
icon: 'warning', console.warn("OpenCV not ready yet");
title: 'Deteksi Gagal', defaultCorners(w, h);
text: 'Dokumen tidak terdeteksi. Silakan atur sudut secara manual.', }
confirmButtonText: 'OK' } catch(e) {
}); console.error("Scanner Error:", e);
} defaultCorners(w, h); // Fallback
} }
} else {
console.warn("OpenCV not ready yet"); $('#scanner-loading').hide();
defaultCorners(w, h); drawOverlay();
if (showAlert) { }
Swal.fire({
icon: 'warning',
title: 'Scanner Tidak Siap',
text: 'OpenCV belum siap. Silakan atur sudut secara manual.',
confirmButtonText: 'OK'
});
}
}
} catch(e) {
console.error("Scanner Error:", e);
defaultCorners(w, h); // Fallback
if (showAlert) {
Swal.fire({
icon: 'error',
title: 'Error Scanner',
text: 'Terjadi kesalahan saat mendeteksi dokumen: ' + e.message,
confirmButtonText: 'OK'
});
}
}
drawOverlay();
}
function setupScannerCanvas() { function defaultCorners(w, h) {
// Resize Canvas to fit screen but keep aspect ratio
var maxWidth = $('#modalScanner .modal-body').width() - 20;
var maxHeight = $('#modalScanner .modal-body').height() - 20;
var scale = Math.min(maxWidth / originalImg.width, maxHeight / originalImg.height);
var w = originalImg.width * scale;
var h = originalImg.height * scale;
canvasImage.width = w;
canvasImage.height = h;
canvasOverlay.width = w;
canvasOverlay.height = h;
// Resize container
$('#scanner-container').css({ width: w, height: h, marginTop: '10px' });
// Draw Image
ctxImg.drawImage(originalImg, 0, 0, w, h);
detectDocument(scale, w, h, false);
$('#scanner-loading').hide();
// No cropper needed - smart scan only
}
function defaultCorners(w, h) {
// Default 20% margin // Default 20% margin
var mX = w * 0.1; var mX = w * 0.1;
var mY = h * 0.1; var mY = h * 0.1;
@@ -566,7 +181,7 @@ window.addEventListener('load', function() {
if (corners.length < 4) return; if (corners.length < 4) return;
// Draw thin cropping lines // Draw Lines
ctxOver.beginPath(); ctxOver.beginPath();
ctxOver.lineWidth = LINE_WIDTH; ctxOver.lineWidth = LINE_WIDTH;
ctxOver.strokeStyle = LINE_COLOR; ctxOver.strokeStyle = LINE_COLOR;
@@ -577,19 +192,14 @@ window.addEventListener('load', function() {
ctxOver.closePath(); ctxOver.closePath();
ctxOver.stroke(); ctxOver.stroke();
// Draw transparent circles with thin outline // Draw Points
ctxOver.fillStyle = POINT_COLOR;
corners.forEach(p => { corners.forEach(p => {
// Draw transparent inner circle
ctxOver.beginPath(); ctxOver.beginPath();
ctxOver.arc(p.x, p.y, POINT_RADIUS, 0, Math.PI * 2); ctxOver.arc(p.x, p.y, POINT_RADIUS, 0, Math.PI * 2);
ctxOver.fillStyle = POINT_COLOR;
ctxOver.fill(); ctxOver.fill();
ctxOver.strokeStyle = 'white';
// Draw thin blue outline ctxOver.lineWidth = 2;
ctxOver.beginPath();
ctxOver.arc(p.x, p.y, POINT_RADIUS, 0, Math.PI * 2);
ctxOver.lineWidth = POINT_OUTLINE_WIDTH;
ctxOver.strokeStyle = POINT_OUTLINE_COLOR;
ctxOver.stroke(); ctxOver.stroke();
}); });
} }
@@ -604,192 +214,106 @@ window.addEventListener('load', function() {
}; };
} }
function isInside(pos, point) { function isInside(pos, point) {
var dx = pos.x - point.x; var dx = pos.x - point.x;
var dy = pos.y - point.y; var dy = pos.y - point.y;
return dx * dx + dy * dy <= POINT_RADIUS * POINT_RADIUS * 2; // Bigger hit area return dx * dx + dy * dy <= POINT_RADIUS * POINT_RADIUS * 2; // Bigger hit area
} }
function getClosestCorner(pos, isTouch) {
var closestIdx = -1;
var closestDist = Infinity;
var radius = POINT_RADIUS * (isTouch ? TOUCH_RADIUS_MULTIPLIER : 1.5); // Larger radius for touch
var radiusSq = radius * radius;
corners.forEach((p, i) => {
var dx = pos.x - p.x;
var dy = pos.y - p.y;
var distSq = dx * dx + dy * dy;
if (distSq < radiusSq && distSq < closestDist) {
closestDist = distSq;
closestIdx = i;
}
});
return closestIdx;
}
canvasOverlay.addEventListener('mousedown', function(e) { canvasOverlay.addEventListener('mousedown', function(e) { handleStart(getMousePos(e)); });
handleStart(getMousePos(e), false); canvasOverlay.addEventListener('touchstart', function(e) { handleStart(getMousePos(e)); e.preventDefault(); }, {passive: false});
e.stopPropagation();
});
canvasOverlay.addEventListener('touchstart', function(e) {
var pos = getMousePos(e);
handleStart(pos, true);
e.preventDefault();
e.stopPropagation();
}, {passive: false});
window.addEventListener('mousemove', function(e) { if(isDragging) handleMove(getMousePos(e)); }); // Window to catch drag out window.addEventListener('mousemove', function(e) { if(isDragging) handleMove(getMousePos(e)); }); // Window to catch drag out
canvasOverlay.addEventListener('touchmove', function(e) { canvasOverlay.addEventListener('touchmove', function(e) { if(isDragging) handleMove(getMousePos(e)); e.preventDefault(); }, {passive: false});
if(isDragging) {
handleMove(getMousePos(e));
e.preventDefault();
e.stopPropagation();
}
}, {passive: false});
window.addEventListener('mouseup', function() { handleEnd(); }); window.addEventListener('mouseup', function() { handleEnd(); });
window.addEventListener('touchend', function() { handleEnd(); }); window.addEventListener('touchend', function() { handleEnd(); });
function handleStart(pos, isTouch = false) { function handleStart(pos) {
activePoint = getClosestCorner(pos, isTouch); activePoint = null;
if (activePoint !== -1) { corners.forEach((p, i) => {
isTouchInteraction = isTouch; if (isInside(pos, p)) {
touchOffset = { x: pos.x - corners[activePoint].x, y: pos.y - corners[activePoint].y }; activePoint = i;
if (isTouch) { isDragging = true;
touchStartPos = pos; }
// Start dragging immediately but handle slop in handleMove });
isDragging = true; }
} else {
isDragging = true;
touchStartPos = null;
}
} else {
activePoint = null;
isDragging = false;
isTouchInteraction = false;
touchStartPos = null;
touchOffset = null;
}
}
function handleMove(pos) { function handleMove(pos) {
if (activePoint !== null) { if (activePoint !== null) {
// Touch slop detection // Constrain to canvas?? Optional but good
if (isTouchInteraction && touchStartPos) { corners[activePoint].x = pos.x;
var dx = pos.x - touchStartPos.x; corners[activePoint].y = pos.y;
var dy = pos.y - touchStartPos.y; drawOverlay();
var distSq = dx * dx + dy * dy; }
if (distSq < TOUCH_SLOP * TOUCH_SLOP) { }
return; // Ignore small movements until slop exceeded
} function handleEnd() {
// Slop exceeded, clear touchStartPos so we don't check again isDragging = false;
touchStartPos = null; activePoint = null;
} }
// --- Rotate Functions ---
function rotateImage(degree) {
var offCanvas = document.createElement('canvas');
var offCtx = offCanvas.getContext('2d');
// Swap Width/Height for 90 degree rotation
offCanvas.width = originalImg.height;
offCanvas.height = originalImg.width;
offCtx.translate(offCanvas.width / 2, offCanvas.height / 2);
offCtx.rotate(degree * Math.PI / 180);
offCtx.drawImage(originalImg, -originalImg.width / 2, -originalImg.height / 2);
// Update originalImg
var rotatedUrl = offCanvas.toDataURL();
originalImg.onload = function() {
initScanner(); // Re-init with new image
}
originalImg.src = rotatedUrl;
}
$('#btnScanRotateLeft').click(function() { rotateImage(-90); });
$('#btnScanRotateRight').click(function() { rotateImage(90); });
// --- Reset Button ---
$('#btnScanReset').click(function() {
initScanner();
});
// --- Save / Extract Button ---
$('#btnScanSave').click(function() {
// Warp Image
try {
// 1. Get raw points relative to Original Image
var scaleX = originalImg.width / canvasImage.width;
var scaleY = originalImg.height / canvasImage.height;
// Apply offset to maintain relative position var tl = { x: corners[0].x * scaleX, y: corners[0].y * scaleY };
if (touchOffset) { var tr = { x: corners[1].x * scaleX, y: corners[1].y * scaleY };
corners[activePoint].x = pos.x - touchOffset.x; var br = { x: corners[2].x * scaleX, y: corners[2].y * scaleY };
corners[activePoint].y = pos.y - touchOffset.y; var bl = { x: corners[3].x * scaleX, y: corners[3].y * scaleY };
} else {
corners[activePoint].x = pos.x;
corners[activePoint].y = pos.y;
}
// Optional: constrain to canvas bounds // 2. Calculate dimensions of the crop area
var w = canvasOverlay.width; var widthTop = Math.hypot(tr.x - tl.x, tr.y - tl.y);
var h = canvasOverlay.height; var widthBottom = Math.hypot(br.x - bl.x, br.y - bl.y);
corners[activePoint].x = Math.max(0, Math.min(w, corners[activePoint].x)); var outputWidth = Math.max(widthTop, widthBottom);
corners[activePoint].y = Math.max(0, Math.min(h, corners[activePoint].y));
drawOverlay(); var heightLeft = Math.hypot(bl.x - tl.x, bl.y - tl.y);
} var heightRight = Math.hypot(br.x - tr.x, br.y - tr.y);
} var outputHeight = Math.max(heightLeft, heightRight);
function handleEnd() { var extractPoints = {
isDragging = false; topLeftCorner: tl,
activePoint = null; topRightCorner: tr,
isTouchInteraction = false; bottomRightCorner: br,
touchStartPos = null; bottomLeftCorner: bl
touchOffset = null; };
}
// --- Rotate Functions ---
function rotateImage(degree) {
var offCanvas = document.createElement('canvas');
var offCtx = offCanvas.getContext('2d');
// Swap Width/Height for 90 degree rotation
offCanvas.width = originalImg.height;
offCanvas.height = originalImg.width;
offCtx.translate(offCanvas.width / 2, offCanvas.height / 2);
offCtx.rotate(degree * Math.PI / 180);
offCtx.drawImage(originalImg, -originalImg.width / 2, -originalImg.height / 2);
// Update originalImg
var rotatedUrl = offCanvas.toDataURL();
originalImg.onload = function() {
// Re-init scanner
setupScannerCanvas();
}
originalImg.src = rotatedUrl;
}
$('#btnScanRotateLeft').click(function() { rotateImage(-90); });
$('#btnScanRotateRight').click(function() { rotateImage(90); });
// --- Reset Button ---
$('#btnScanReset').click(function() {
setupScannerCanvas();
});
// --- Save / Extract Button ---
$('#btnScanSave').click(function() {
try {
var base64;
// Smart Scan Mode - Use jscanify // 3. Extract with dynamic dimensions
// 1. Get raw points relative to Original Image var resultCanvas = scanner.extractPaper(originalImg, outputWidth, outputHeight, extractPoints);
var scaleX = originalImg.width / canvasImage.width; var base64 = resultCanvas.toDataURL('image/jpeg');
var scaleY = originalImg.height / canvasImage.height;
var tl = { x: corners[0].x * scaleX, y: corners[0].y * scaleY };
var tr = { x: corners[1].x * scaleX, y: corners[1].y * scaleY };
var br = { x: corners[2].x * scaleX, y: corners[2].y * scaleY };
var bl = { x: corners[3].x * scaleX, y: corners[3].y * scaleY };
// 2. Calculate dimensions of the crop area
var widthTop = Math.hypot(tr.x - tl.x, tr.y - tl.y);
var widthBottom = Math.hypot(br.x - bl.x, br.y - bl.y);
var outputWidth = Math.max(widthTop, widthBottom);
var heightLeft = Math.hypot(bl.x - tl.x, bl.y - tl.y);
var heightRight = Math.hypot(br.x - tr.x, br.y - tr.y);
var outputHeight = Math.max(heightLeft, heightRight);
var extractPoints = {
topLeftCorner: tl,
topRightCorner: tr,
bottomRightCorner: br,
bottomLeftCorner: bl
};
// 3. Extract with dynamic dimensions
var resultCanvas = null;
if (!scanner || !scanner.extractPaper) {
// Try to initialize scanner if not ready
initScanner();
}
if (scanner && scanner.extractPaper) {
resultCanvas = scanner.extractPaper(originalImg, outputWidth, outputHeight, extractPoints);
} else {
alert("Scanner library not loaded. Please refresh the page.");
return;
}
base64 = resultCanvas.toDataURL('image/jpeg');
if (window.handleScannerResult) { if (window.handleScannerResult) {
window.handleScannerResult(base64); window.handleScannerResult(base64);
@@ -797,18 +321,10 @@ window.addEventListener('load', function() {
scannerModal.modal('hide'); scannerModal.modal('hide');
} catch (e) { } catch (e) {
alert("Gagal memproses gambar: " + e.message); alert("Gagal memproses gambar: " + e.message);
console.error(e); }
} });
});
// Show mobile help if touch device
if (IS_TOUCH_DEVICE) {
$('#mobile-help').removeClass('d-none');
}
// Mode is always smart scan now
}, 500); }); });
</script> });
</script>

View File

0
unused/build/js/.jscsrc → build/js/.jscsrc Executable file → Normal file
View File

0
unused/build/js/AdminLTE.js → build/js/AdminLTE.js Executable file → Normal file
View File

View File

View File

View File

View File

0
unused/build/js/Dropdown.js → build/js/Dropdown.js Executable file → Normal file
View File

0
unused/build/js/Layout.js → build/js/Layout.js Executable file → Normal file
View File

0
unused/build/js/PushMenu.js → build/js/PushMenu.js Executable file → Normal file
View File

View File

0
unused/build/js/Toasts.js → build/js/Toasts.js Executable file → Normal file
View File

0
unused/build/js/TodoList.js → build/js/TodoList.js Executable file → Normal file
View File

0
unused/build/js/Treeview.js → build/js/Treeview.js Executable file → Normal file
View File

View File

View File

0
unused/build/npm/Plugins.js → build/npm/Plugins.js Executable file → Normal file
View File

0
unused/build/npm/Publish.js → build/npm/Publish.js Executable file → Normal file
View File

0
unused/build/scss/.csslintrc → build/scss/.csslintrc Executable file → Normal file
View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

0
unused/build/scss/_navs.scss → build/scss/_navs.scss Executable file → Normal file
View File

View File

View File

View File

View File

Some files were not shown because too many files have changed in this diff Show More