diff --git a/api_members.php b/api_members.php
new file mode 100644
index 0000000..38973ef
--- /dev/null
+++ b/api_members.php
@@ -0,0 +1,102 @@
+ false, 'error' => '', 'members' => []];
+
+// Get action from request, default to 'list'
+$action = $_REQUEST['action'] ?? 'list';
+
+
+try {
+ $pdo = db();
+
+ switch ($action) {
+ case 'list':
+ $stmt = $pdo->query("SELECT id, name, nik, address, phone FROM members ORDER BY created_at DESC");
+ $response['members'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
+ $response['success'] = true;
+ break;
+
+ case 'add':
+ // Simple validation
+ if (empty($_POST['name']) || empty($_POST['nik'])) {
+ throw new Exception('Nama dan NIK tidak boleh kosong.');
+ }
+
+ $sql = "INSERT INTO members (name, nik, address, phone) VALUES (:name, :nik, :address, :phone)";
+ $stmt = $pdo->prepare($sql);
+
+ $stmt->execute([
+ ':name' => $_POST['name'],
+ ':nik' => $_POST['nik'],
+ ':address' => $_POST['address'] ?? '',
+ ':phone' => $_POST['phone'] ?? ''
+ ]);
+
+ $response['success'] = true;
+ break;
+
+ case 'get':
+ if (empty($_GET['id'])) {
+ throw new Exception('ID anggota tidak valid.');
+ }
+ $stmt = $pdo->prepare("SELECT id, name, nik, address, phone FROM members WHERE id = :id");
+ $stmt->execute([':id' => $_GET['id']]);
+ $member = $stmt->fetch(PDO::FETCH_ASSOC);
+ if (!$member) {
+ throw new Exception('Anggota tidak ditemukan.');
+ }
+ $response['member'] = $member;
+ $response['success'] = true;
+ break;
+
+ case 'update':
+ if (empty($_POST['id']) || empty($_POST['name']) || empty($_POST['nik'])) {
+ throw new Exception('ID, Nama, dan NIK tidak boleh kosong.');
+ }
+
+ $sql = "UPDATE members SET name = :name, nik = :nik, address = :address, phone = :phone WHERE id = :id";
+ $stmt = $pdo->prepare($sql);
+
+ $stmt->execute([
+ ':id' => $_POST['id'],
+ ':name' => $_POST['name'],
+ ':nik' => $_POST['nik'],
+ ':address' => $_POST['address'] ?? '',
+ ':phone' => $_POST['phone'] ?? ''
+ ]);
+
+ $response['success'] = true;
+ break;
+
+ case 'delete':
+ if (empty($_POST['id'])) {
+ throw new Exception('ID anggota tidak valid.');
+ }
+
+ $sql = "DELETE FROM members WHERE id = :id";
+ $stmt = $pdo->prepare($sql);
+ $stmt->execute([':id' => $_POST['id']]);
+
+ $response['success'] = true;
+ break;
+
+ default:
+ throw new Exception('Aksi tidak valid.');
+ }
+
+} catch (PDOException $e) {
+ $response['error'] = 'Database error: ' . $e->getMessage();
+ // Check for duplicate entry
+ if ($e->getCode() == 23000) {
+ $response['error'] = 'NIK sudah terdaftar.';
+ }
+} catch (Exception $e) {
+ $response['error'] = $e->getMessage();
+}
+
+echo json_encode($response);
diff --git a/assets/css/custom.css b/assets/css/custom.css
new file mode 100644
index 0000000..5511f00
--- /dev/null
+++ b/assets/css/custom.css
@@ -0,0 +1,24 @@
+
+body {
+ background-color: #f8f9fa;
+}
+
+.card {
+ border: none;
+ box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
+}
+
+.modal-header {
+ border-bottom: none;
+}
+
+.modal-footer {
+ border-top: none;
+}
+
+.toast {
+ position: fixed;
+ top: 20px;
+ right: 20px;
+ z-index: 1055;
+}
diff --git a/assets/js/main.js b/assets/js/main.js
new file mode 100644
index 0000000..733df19
--- /dev/null
+++ b/assets/js/main.js
@@ -0,0 +1,198 @@
+document.addEventListener('DOMContentLoaded', function () {
+ const addMemberModal = new bootstrap.Modal(document.getElementById('addMemberModal'));
+ const editMemberModal = new bootstrap.Modal(document.getElementById('editMemberModal'));
+ const addMemberForm = document.getElementById('addMemberForm');
+ const editMemberForm = document.getElementById('editMemberForm');
+ const memberTableBody = document.getElementById('memberTableBody');
+
+ // Function to show a toast notification
+ function showToast(message, success = true) {
+ const toastContainer = document.getElementById('toastContainer');
+ const toast = document.createElement('div');
+ toast.className = `toast align-items-center text-white ${success ? 'bg-success' : 'bg-danger'} border-0 show`;
+ toast.role = 'alert';
+ toast.ariaLive = 'assertive';
+ toast.ariaAtomic = 'true';
+
+ toast.innerHTML = `
+
+ `;
+ toastContainer.appendChild(toast);
+
+ const bsToast = new bootstrap.Toast(toast);
+ bsToast.show();
+
+ setTimeout(() => {
+ bsToast.hide();
+ setTimeout(() => {
+ toast.remove();
+ }, 500);
+ }, 3000);
+ }
+
+ function loadMembers() {
+ fetch('api_members.php?action=list')
+ .then(response => response.json())
+ .then(data => {
+ memberTableBody.innerHTML = '';
+ if (data.success) {
+ if (data.members.length === 0) {
+ memberTableBody.innerHTML = '| Belum ada anggota. |
';
+ } else {
+ data.members.forEach(member => {
+ const row = `
+ | ${member.id} |
+ ${escapeHTML(member.name)} |
+ ${escapeHTML(member.nik)} |
+ ${escapeHTML(member.address)} |
+ ${escapeHTML(member.phone)} |
+
+
+
+ |
+
`;
+ memberTableBody.innerHTML += row;
+ });
+ }
+ // Update total members card
+ document.getElementById('totalMembers').innerText = data.members.length;
+ } else {
+ memberTableBody.innerHTML = `| Gagal memuat data: ${data.error} |
`;
+ }
+ })
+ .catch(error => {
+ memberTableBody.innerHTML = `| Error: ${error} |
`;
+ });
+ }
+
+ // Helper to prevent XSS
+ function escapeHTML(str) {
+ return str.toString().replace(/[&<>"']/g, function (tag) {
+ var chars = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": '''
+ };
+ return chars[tag] || tag;
+ });
+ }
+
+ // Handle Add Member form submission
+ addMemberForm.addEventListener('submit', function (event) {
+ event.preventDefault();
+
+ const formData = new FormData(addMemberForm);
+ formData.append('action', 'add');
+
+ fetch('api_members.php', {
+ method: 'POST',
+ body: formData
+ })
+ .then(response => response.json())
+ .then(data => {
+ if (data.success) {
+ showToast('Anggota berhasil ditambahkan!');
+ addMemberModal.hide();
+ addMemberForm.reset();
+ loadMembers();
+ } else {
+ showToast('Gagal menambahkan anggota: ' + data.error, false);
+ }
+ })
+ .catch(error => {
+ showToast('Terjadi kesalahan. Silakan coba lagi.', false);
+ });
+ });
+
+''' // Delegated event listener for edit and delete buttons
+ memberTableBody.addEventListener('click', function (event) {
+ const editButton = event.target.closest('.btn-edit');
+ const deleteButton = event.target.closest('.btn-delete');
+
+ if (editButton) {
+ const memberId = editButton.dataset.id;
+ openEditModal(memberId);
+ } else if (deleteButton) {
+ const memberId = deleteButton.dataset.id;
+ deleteMember(memberId);
+ }
+ });
+
+ // Function to open and populate the edit modal
+ function openEditModal(memberId) {
+ fetch(`api_members.php?action=get&id=${memberId}`)
+ .then(response => response.json())
+ .then(data => {
+ if (data.success) {
+ const member = data.member;
+ document.getElementById('editMemberId').value = member.id;
+ document.getElementById('editName').value = member.name;
+ document.getElementById('editNik').value = member.nik;
+ document.getElementById('editAddress').value = member.address;
+ document.getElementById('editPhone').value = member.phone;
+ editMemberModal.show();
+ } else {
+ showToast('Gagal mengambil data anggota: ' + data.error, false);
+ }
+ })
+ .catch(() => showToast('Terjadi kesalahan jaringan.', false));
+ }
+
+ // Handle Edit Member form submission
+ editMemberForm.addEventListener('submit', function (event) {
+ event.preventDefault();
+
+ const formData = new FormData(editMemberForm);
+ formData.append('action', 'update');
+
+ fetch('api_members.php', {
+ method: 'POST',
+ body: new URLSearchParams(formData)
+ })
+ .then(response => response.json())
+ .then(data => {
+ if (data.success) {
+ showToast('Data anggota berhasil diperbarui!');
+ editMemberModal.hide();
+ loadMembers();
+ } else {
+ showToast('Gagal memperbarui data: ' + data.error, false);
+ }
+ })
+ .catch(() => {
+ showToast('Terjadi kesalahan. Silakan coba lagi.', false);
+ });
+ });
+
+ // Function to delete a member
+ function deleteMember(memberId) {
+ if (confirm('Apakah Anda yakin ingin menghapus anggota ini?')) {
+ const formData = new FormData();
+ formData.append('action', 'delete');
+ formData.append('id', memberId);
+
+ fetch('api_members.php', {
+ method: 'POST',
+ body: new URLSearchParams(formData)
+ })
+ .then(response => response.json())
+ .then(data => {
+ if (data.success) {
+ showToast('Anggota berhasil dihapus.');
+ loadMembers();
+ } else {
+ showToast('Gagal menghapus anggota: ' + data.error, false);
+ }
+ })
+ .catch(() => showToast('Terjadi kesalahan jaringan.', false));
+ }
+ }
+
+ // Initial load
+ loadMembers();
+});''
diff --git a/db_setup.php b/db_setup.php
new file mode 100644
index 0000000..c0b4d9c
--- /dev/null
+++ b/db_setup.php
@@ -0,0 +1,30 @@
+exec($sql);
+
+ // You can add more table creations here in the future
+
+ } catch (PDOException $e) {
+ // In a real app, you'd want to log this error
+ // For this example, we'll just output it.
+ die("DB setup failed: " . $e->getMessage());
+ }
+}
+
+// Run the setup
+setup_database();
diff --git a/index.php b/index.php
index 7205f3d..b454588 100644
--- a/index.php
+++ b/index.php
@@ -1,150 +1,197 @@
-
-
+
+
-
-
- New Style
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
Analyzing your requirements and generating your website…
-
- Loading…
-
-
= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.
-
This page will update automatically as the plan is implemented.
-
Runtime: PHP = htmlspecialchars($phpVersion) ?> — UTC = htmlspecialchars($now) ?>
+
+
+
+
+ Dashboard
+
+
+
+
+
+
+
+
Total Simpanan
+
Rp 0
+
+
+
+
+
+
+
Total Pinjaman
+
Rp 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | ID |
+ Nama |
+ NIK |
+ Alamat |
+ Telepon |
+ Aksi |
+
+
+
+
+ | Memuat data... |
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
-
+
\ No newline at end of file