diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..d84348d
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,18 @@
+# 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*
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..d147c4a
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,38 @@
+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"]
\ No newline at end of file
diff --git a/INSTALL_DOCKER.md b/INSTALL_DOCKER.md
new file mode 100644
index 0000000..e8a59cc
--- /dev/null
+++ b/INSTALL_DOCKER.md
@@ -0,0 +1,67 @@
+# SIDAK Docker Setup
+
+Aplikasi SIDAK berjalan dengan Docker Compose.
+
+## Services yang berjalan:
+1. **Web App** - PHP Apache: http://localhost:8500
+2. **Database** - MariaDB: localhost:3307
+3. **phpMyAdmin** - Database admin: http://localhost:8080
+
+## Cara menjalankan:
+
+### 1. Start aplikasi:
+```bash
+docker compose up -d
+```
+
+### 2. Stop aplikasi:
+```bash
+docker compose down
+```
+
+### 3. Stop dan hapus data:
+```bash
+docker compose down -v
+```
+
+### 4. Melihat logs:
+```bash
+docker compose logs -f
+```
+
+## Credentials:
+
+### Aplikasi SIDAK:
+- URL: http://localhost:8500
+- Admin: `admin` / `admin`
+- Kaur: `kaur` / `kaur`
+
+### Database:
+- Host: `db` (dalam container) atau `localhost:3307` (dari host)
+- Database: `data_penduduk`
+- User: `sidak_user`
+- Password: `sidak_pass`
+- Root password: `rootpassword`
+
+### phpMyAdmin:
+- URL: http://localhost:8080
+- Server: `db`
+- Username: `sidak_user` atau `root`
+- Password: `sidak_pass` atau `rootpassword`
+
+## Troubleshooting:
+
+1. **Port conflict**: Jika port 8500, 8080, atau 3307 sudah digunakan, ubah di `docker-compose.yml`
+2. **Database connection error**: Tunggu 30 detik untuk database startup
+3. **PHP extensions**: Extensions mysqli, pdo, pdo_mysql sudah diinstall otomatis
+4. **File permissions**: Semua file di-mount dari host ke container
+
+## Struktur file Docker:
+- `docker-compose.yml` - Konfigurasi services
+- `init.sql` - Skema dan data awal database
+- `inc/koneksi.php` - Konfigurasi koneksi database (menggunakan env variables)
+
+## Catatan:
+- Perubahan file PHP langsung terlihat (hot reload)
+- Data database persist di volume Docker `sidak_mariadb-data`
+- Untuk development, edit file langsung di host
\ No newline at end of file
diff --git a/README_DOCKER.md b/README_DOCKER.md
new file mode 100644
index 0000000..7f813c1
--- /dev/null
+++ b/README_DOCKER.md
@@ -0,0 +1,74 @@
+# SIDAK Application with Docker
+
+## Prerequisites
+- Docker
+- Docker Compose
+
+## Quick Start
+
+1. **Start the application:**
+ ```bash
+ docker-compose up -d
+ ```
+
+2. **Access the application:**
+ - Web application: http://localhost:8500
+ - phpMyAdmin: http://localhost:8080
+ - MySQL/MariaDB: localhost:3306
+
+3. **Default credentials:**
+ - **Application:**
+ - Admin: `admin` / `admin`
+ - Kaur: `kaur` / `kaur`
+ - **Database:**
+ - Host: `db`
+ - Database: `data_penduduk`
+ - User: `sidak_user`
+ - Password: `sidak_pass`
+ - Root password: `rootpassword`
+
+4. **Stop the application:**
+ ```bash
+ docker-compose down
+ ```
+
+5. **Stop and remove volumes (clears all data):**
+ ```bash
+ docker-compose down -v
+ ```
+
+## Services
+
+1. **web** - PHP Apache application
+ - Port: 8500
+ - Directory mounted: `/var/www/html`
+ - Environment variables: DB_HOST, DB_USER, DB_PASS, DB_NAME
+
+2. **db** - MariaDB database
+ - Port: 3306
+ - Database: `data_penduduk`
+ - Auto-initialized with schema and sample data
+ - Data persisted in volume: `mariadb-data`
+
+3. **phpmyadmin** - Database management
+ - Port: 8080
+ - Connect to host: `db`
+
+## Development
+
+- **Hot reload:** Changes to PHP files are reflected immediately due to volume mounting
+- **Database persistence:** Data is preserved between container restarts
+- **Build custom image:** `docker-compose build`
+
+## Troubleshooting
+
+1. **Port conflicts:** Check if ports 8500, 8080, or 3306 are already in use
+2. **Database connection issues:** Wait a few seconds for database to initialize
+3. **View logs:** `docker-compose logs -f`
+4. **Reset database:** `docker-compose down -v && docker-compose up -d`
+
+## File Structure
+- `docker-compose.yml` - Service definitions
+- `Dockerfile` - PHP Apache image configuration
+- `init.sql` - Database initialization script
+- `.dockerignore` - Files to exclude from build context
\ No newline at end of file
diff --git a/admin/kartu/add_kartu.php b/admin/kartu/add_kartu.php
index 8cf19cd..44027dd 100644
--- a/admin/kartu/add_kartu.php
+++ b/admin/kartu/add_kartu.php
@@ -30,9 +30,9 @@
@@ -46,10 +46,10 @@
@@ -238,27 +238,28 @@ window.addEventListener('load', function() {
}
//mulai proses simpan data
- $no_kk = $_POST['no_kk'];
- $cek_kk = mysqli_query($koneksi, "SELECT * FROM tb_kk WHERE no_kk='$no_kk'");
- if(mysqli_num_rows($cek_kk) > 0){
- echo "";
- return;
- }
-
- // Sanitize Inputs
- $no_kk = mysqli_real_escape_string($koneksi, $_POST['no_kk']);
- $kepala = mysqli_real_escape_string($koneksi, $_POST['kepala']);
- $desa = mysqli_real_escape_string($koneksi, $_POST['desa']);
- $rt = mysqli_real_escape_string($koneksi, $_POST['rt']);
- $rw = mysqli_real_escape_string($koneksi, $_POST['rw']);
- $kec = mysqli_real_escape_string($koneksi, $_POST['kec']);
- $kab = mysqli_real_escape_string($koneksi, $_POST['kab']);
- $prov = mysqli_real_escape_string($koneksi, $_POST['prov']);
+ $no_kk_raw = trim($_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_clean'");
+ if(mysqli_num_rows($cek_kk) > 0){
+ echo "";
+ return;
+ }
+
+ // Sanitize Inputs
+ $no_kk = $no_kk_clean;
+ $kepala = mysqli_real_escape_string($koneksi, trim($_POST['kepala']));
+ $desa = mysqli_real_escape_string($koneksi, trim($_POST['desa']));
+ $rt = mysqli_real_escape_string($koneksi, trim($_POST['rt']));
+ $rw = mysqli_real_escape_string($koneksi, trim($_POST['rw']));
+ $kec = mysqli_real_escape_string($koneksi, trim($_POST['kec']));
+ $kab = mysqli_real_escape_string($koneksi, trim($_POST['kab']));
+ $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 (
'$no_kk',
@@ -272,43 +273,67 @@ window.addEventListener('load', function() {
'$nama_file')";
$query_simpan = mysqli_query($koneksi, $sql_simpan);
- // Process Auto-Linking Members
- $linked_count = 0;
- $failed_count = 0;
-
- if ($query_simpan && !empty($_POST['anggota_json'])) {
- $id_kk_baru = mysqli_insert_id($koneksi);
- $anggota_list = json_decode($_POST['anggota_json'], true);
-
- if (is_array($anggota_list)) {
- foreach ($anggota_list as $mem) {
- $nik_mem = mysqli_real_escape_string($koneksi, $mem['nik']);
- $hub_mem = mysqli_real_escape_string($koneksi, $mem['hubungan']);
-
- // Search Resident by NIK
- $sql_cek_pend = "SELECT id_pend FROM tb_pdd WHERE nik='$nik_mem'";
- $q_cek_pend = mysqli_query($koneksi, $sql_cek_pend);
- if ($row_pend = mysqli_fetch_assoc($q_cek_pend)) {
- $id_pend_found = $row_pend['id_pend'];
-
- // Insert into tb_anggota
- $sql_add_ang = "INSERT INTO tb_anggota (id_kk, id_pend, hubungan) VALUES ('$id_kk_baru', '$id_pend_found', '$hub_mem')";
- mysqli_query($koneksi, $sql_add_ang);
- $linked_count++;
- } else {
- $failed_count++;
- }
- }
- }
- }
+ // Process Auto-Linking Members
+ $linked_count = 0;
+ $failed_count = 0;
+ $failed_members = []; // Store failed members for detailed feedback
+
+ if ($query_simpan && !empty($_POST['anggota_json'])) {
+ $id_kk_baru = mysqli_insert_id($koneksi);
+ $anggota_list = json_decode($_POST['anggota_json'], true);
+
+ if (is_array($anggota_list)) {
+ foreach ($anggota_list as $mem) {
+ $nik_mem = mysqli_real_escape_string($koneksi, $mem['nik']);
+ $nama_mem = isset($mem['nama']) ? mysqli_real_escape_string($koneksi, $mem['nama']) : '';
+ $hub_mem = mysqli_real_escape_string($koneksi, $mem['hubungan']);
+
+ // Search Resident by NIK
+ $sql_cek_pend = "SELECT id_pend FROM tb_pdd WHERE nik='$nik_mem'";
+ $q_cek_pend = mysqli_query($koneksi, $sql_cek_pend);
+ if ($row_pend = mysqli_fetch_assoc($q_cek_pend)) {
+ $id_pend_found = $row_pend['id_pend'];
+
+ // Insert into tb_anggota
+ $sql_add_ang = "INSERT INTO tb_anggota (id_kk, id_pend, hubungan) VALUES ('$id_kk_baru', '$id_pend_found', '$hub_mem')";
+ mysqli_query($koneksi, $sql_add_ang);
+ $linked_count++;
+ } else {
+ $failed_count++;
+ $failed_members[] = ['nik' => $nik_mem, 'nama' => $nama_mem, 'hubungan' => $hub_mem];
+ }
+ }
+ }
+ }
mysqli_close($koneksi);
- if ($query_simpan) {
- $msg_add = "";
- if($linked_count > 0 || $failed_count > 0) {
- $msg_add = "
Anggota Terhubung: $linked_count
Tidak Ditemukan: $failed_count";
- }
+ if ($query_simpan) {
+ $msg_add = "";
+ if($linked_count > 0 || $failed_count > 0) {
+ $msg_add = "
Anggota Terhubung: $linked_count
Tidak Ditemukan: $failed_count";
+ // Add detailed failed members list if any
+ if (!empty($failed_members)) {
+ $msg_add .= "
Detail Anggota Tidak Ditemukan:
";
+ $msg_add .= "";
+ 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 = "
Tambah";
+ $msg_add .= "•
" . htmlspecialchars($fm['nik']) . " - " . htmlspecialchars($fm['nama']) . " (" . htmlspecialchars($fm['hubungan']) . ") $add_link
";
+ }
+ $msg_add .= "
";
+ $msg_add .= "Tambahkan data penduduk yang belum terdaftar melalui menu Tambah Penduduk.";
+ }
+ }
echo "";
- return;
- }
+ $nik = mysqli_real_escape_string($koneksi, trim($_POST['nik']));
+ $cek_nik = mysqli_query($koneksi, "SELECT * FROM tb_pdd WHERE nik='$nik'");
+ if(mysqli_num_rows($cek_nik) > 0){
+ echo "";
+ return;
+ }
+
+ // Sanitize Input to prevent SQL Injection & Syntax Errors
+ $nama = mysqli_real_escape_string($koneksi, trim($_POST['nama']));
+ $tempat_lh = mysqli_real_escape_string($koneksi, trim($_POST['tempat_lh']));
+ $tgl_lh = mysqli_real_escape_string($koneksi, trim($_POST['tgl_lh']));
+ $jekel = mysqli_real_escape_string($koneksi, trim($_POST['jekel']));
+ $desa = mysqli_real_escape_string($koneksi, trim($_POST['desa']));
+ $rt = mysqli_real_escape_string($koneksi, trim($_POST['rt']));
+ $rw = mysqli_real_escape_string($koneksi, trim($_POST['rw']));
+ $agama = mysqli_real_escape_string($koneksi, trim($_POST['agama']));
+ $kawin = mysqli_real_escape_string($koneksi, trim($_POST['kawin']));
+ $pekerjaan = mysqli_real_escape_string($koneksi, trim($_POST['pekerjaan']));
+ $kecamatan = mysqli_real_escape_string($koneksi, trim($_POST['kecamatan']));
+ $kabupaten = mysqli_real_escape_string($koneksi, trim($_POST['kabupaten']));
+ $provinsi = mysqli_real_escape_string($koneksi, trim($_POST['provinsi']));
+ $kewarganegaraan = mysqli_real_escape_string($koneksi, trim($_POST['kewarganegaraan']));
- // Sanitize Input to prevent SQL Injection & Syntax Errors
- $nik = mysqli_real_escape_string($koneksi, $_POST['nik']);
- $nama = mysqli_real_escape_string($koneksi, $_POST['nama']);
- $tempat_lh = mysqli_real_escape_string($koneksi, $_POST['tempat_lh']);
- $tgl_lh = mysqli_real_escape_string($koneksi, $_POST['tgl_lh']);
- $jekel = mysqli_real_escape_string($koneksi, $_POST['jekel']);
- $desa = mysqli_real_escape_string($koneksi, $_POST['desa']);
- $rt = mysqli_real_escape_string($koneksi, $_POST['rt']);
- $rw = mysqli_real_escape_string($koneksi, $_POST['rw']);
- $agama = mysqli_real_escape_string($koneksi, $_POST['agama']);
- $kawin = mysqli_real_escape_string($koneksi, $_POST['kawin']);
- $pekerjaan = mysqli_real_escape_string($koneksi, $_POST['pekerjaan']);
- $kecamatan = mysqli_real_escape_string($koneksi, $_POST['kecamatan']);
- $kabupaten = mysqli_real_escape_string($koneksi, $_POST['kabupaten']);
- $provinsi = mysqli_real_escape_string($koneksi, $_POST['provinsi']);
- $kewarganegaraan = mysqli_real_escape_string($koneksi, $_POST['kewarganegaraan']);
-
- $sql_simpan = "INSERT INTO tb_pdd (nik, nama, tempat_lh, tgl_lh, jekel, desa, rt, rw, agama, kawin, pekerjaan, foto_ktp, status, kecamatan, kabupaten, provinsi, kewarganegaraan) VALUES (
- '$nik',
- '$nama',
- '$tempat_lh',
- '$tgl_lh',
- '$jekel',
- '$desa',
- '$rt',
- '$rw',
- '$agama',
- '$kawin',
- '$pekerjaan',
- '$nama_file',
- 'Ada',
- '$kecamatan',
- '$kabupaten',
- '$provinsi',
- '$kewarganegaraan')";
- $query_simpan = mysqli_query($koneksi, $sql_simpan);
- mysqli_close($koneksi);
-
- if ($query_simpan) {
- echo "";
- }else{
- echo "";
- }}
+ $sql_simpan = "INSERT INTO tb_pdd (nik, nama, tempat_lh, tgl_lh, jekel, desa, rt, rw, agama, kawin, pekerjaan, foto_ktp, status, kecamatan, kabupaten, provinsi, kewarganegaraan) VALUES (
+ '$nik',
+ '$nama',
+ '$tempat_lh',
+ '$tgl_lh',
+ '$jekel',
+ '$desa',
+ '$rt',
+ '$rw',
+ '$agama',
+ '$kawin',
+ '$pekerjaan',
+ '$nama_file',
+ 'Ada',
+ '$kecamatan',
+ '$kabupaten',
+ '$provinsi',
+ '$kewarganegaraan')";
+ $query_simpan = mysqli_query($koneksi, $sql_simpan);
+
+ if ($query_simpan) {
+ $id_pend_baru = mysqli_insert_id($koneksi);
+
+ // KTP → KK: Cari KK yang cocok berdasarkan alamat
+ $sql_cari_kk = "SELECT k.id_kk, k.no_kk, k.kepala, k.desa, k.rt, k.rw
+ FROM tb_kk k
+ WHERE k.desa='$desa' AND k.rt='$rt' AND k.rw='$rw'
+ AND k.kec='$kecamatan' AND k.kab='$kabupaten' AND k.prov='$provinsi'";
+ $q_cari_kk = mysqli_query($koneksi, $sql_cari_kk);
+ $kk_cocok = mysqli_fetch_assoc($q_cari_kk);
+
+ if ($kk_cocok) {
+ // Tawarkan untuk menghubungkan dengan KK
+ $no_kk = $kk_cocok['no_kk'];
+ $kepala_kk = $kk_cocok['kepala'];
+ $id_kk = $kk_cocok['id_kk'];
+
+ // Cek apakah sudah terhubung
+ $sql_cek_hubungan = "SELECT * FROM tb_anggota WHERE id_kk='$id_kk' AND id_pend='$id_pend_baru'";
+ $q_cek_hubungan = mysqli_query($koneksi, $sql_cek_hubungan);
+
+ if (mysqli_num_rows($q_cek_hubungan) == 0) {
+ // Simpan sementara data untuk konfirmasi JavaScript
+ $_SESSION['kk_link_data'] = [
+ 'id_pend' => $id_pend_baru,
+ 'id_kk' => $id_kk,
+ 'no_kk' => $no_kk,
+ 'kepala_kk' => $kepala_kk,
+ 'nama_pend' => $nama
+ ];
+
+ echo "";
+ } else {
+ // Sudah terhubung
+ echo "";
+ }
+ } else {
+ // Tidak ada KK yang cocok
+ echo "";
+ }
+ } else {
+ echo "";
+ }
+ mysqli_close($koneksi);
+ }
//selesai proses simpan data
diff --git a/admin/pend/link_to_kk.php b/admin/pend/link_to_kk.php
new file mode 100644
index 0000000..5a59630
--- /dev/null
+++ b/admin/pend/link_to_kk.php
@@ -0,0 +1,62 @@
+ 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);
+?>
\ No newline at end of file
diff --git a/admin/scanner_modal.php b/admin/scanner_modal.php
index 86ef2d3..5a3690f 100644
--- a/admin/scanner_modal.php
+++ b/admin/scanner_modal.php
@@ -20,11 +20,11 @@
+
+