419 lines
21 KiB
PHP
419 lines
21 KiB
PHP
<?php
|
|
session_start();
|
|
include 'koneksi.php';
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'save_account') {
|
|
$id = $_POST['id_account'] ?? '';
|
|
$name = $_POST['name'] ?? '';
|
|
$username = $_POST['username'] ?? '';
|
|
$password = $_POST['password'] ?? '';
|
|
|
|
header('Content-Type: application/json');
|
|
try {
|
|
if (empty($id)) {
|
|
if (empty($password)) {
|
|
echo json_encode(['status'=>'error', 'message'=>'Password is required for new account.']);
|
|
exit;
|
|
}
|
|
$enc = password_hash($password, PASSWORD_DEFAULT);
|
|
$stmt = $pdo->prepare("INSERT INTO account (name, username, password, password_enc, status) VALUES (?, ?, ?, ?, 'approved')");
|
|
$stmt->execute([$name, $username, $enc, $password]);
|
|
} else {
|
|
if (!empty($password)) {
|
|
$enc = password_hash($password, PASSWORD_DEFAULT);
|
|
$stmt = $pdo->prepare("UPDATE account SET name=?, username=?, password=?, password_enc=? WHERE id_account=?");
|
|
$stmt->execute([$name, $username, $enc, $password, $id]);
|
|
} else {
|
|
$stmt = $pdo->prepare("UPDATE account SET name=?, username=? WHERE id_account=?");
|
|
$stmt->execute([$name, $username, $id]);
|
|
}
|
|
}
|
|
echo json_encode(['status'=>'success', 'message' => 'Account saved successfully']);
|
|
} catch(Exception $e) {
|
|
echo json_encode(['status'=>'error', 'message'=>$e->getMessage()]);
|
|
}
|
|
exit;
|
|
}
|
|
|
|
include 'header.php';
|
|
?>
|
|
<!-- partial -->
|
|
<div class="main-panel">
|
|
<div class="content-wrapper">
|
|
<div class="row">
|
|
<div class="col-md-12 grid-margin">
|
|
<div class="row">
|
|
<div class="col-12 col-xl-8 mb-4 mb-xl-0">
|
|
<h3 class="font-weight-bold">Account Operations</h3>
|
|
<h6 class="font-weight-normal mb-0">Manage operational account data, <span class="text-primary">Managing active accounts!</span></h6>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ==================== CARD 1: FORM ==================== -->
|
|
<div class="row">
|
|
<div class="col-md-12 grid-margin stretch-card">
|
|
<div class="card" style="border:1px solid #e5e9f0;box-shadow:none;border-radius:12px;">
|
|
<div class="card-body" style="padding:28px;">
|
|
<p class="card-title mb-0" id="formTitle">Add Account</p>
|
|
<p class="text-muted mt-1 mb-4" style="font-size:12px;" id="formSubtitle">Create a new account.</p>
|
|
|
|
<form id="formAccount">
|
|
<input type="hidden" name="action" value="save_account">
|
|
<input type="hidden" name="id_account" id="input_id_account" value="">
|
|
<div class="row">
|
|
<div class="col-md-4 mb-3">
|
|
<label class="frm-lbl">Name</label>
|
|
<input type="text" name="name" id="input_name" class="form-control form-control-sm" required style="border-color:#e5e9f0; height:38px;">
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<label class="frm-lbl">Username</label>
|
|
<input type="text" name="username" id="input_username" class="form-control form-control-sm" required style="border-color:#e5e9f0; height:38px;">
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<label class="frm-lbl">Password</label>
|
|
<div class="input-group">
|
|
<input type="password" name="password" id="input_password" class="form-control form-control-sm" style="border-color:#e5e9f0; height:38px;">
|
|
<div class="input-group-append">
|
|
<span class="input-group-text eye-toggle bg-white" data-target="input_password" style="cursor: pointer; border-radius: 0 4px 4px 0; border: 1px solid #e5e9f0; border-left: none; height: 38px; display: flex; align-items: center; padding: 0 12px;"><i class="mdi mdi-eye-off text-muted"></i></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row mt-2">
|
|
<div class="col-md-12 text-right">
|
|
<button type="button" class="btn btn-light btn-sm mr-2" id="btnCancelEdit" style="display:none; font-weight:bold; border-radius:6px;" onclick="cancelEdit()">Cancel Edit</button>
|
|
<button type="submit" class="btn btn-primary btn-sm" id="btnSaveAccount" style="font-weight:bold; border-radius:6px; padding:8px 16px;"><i class="mdi mdi-content-save mr-1"></i>Save</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ==================== CARD 2: TABLE ==================== -->
|
|
<div class="row">
|
|
<div class="col-md-12 grid-margin stretch-card">
|
|
<div class="card" style="border: 1px solid #e2e8f0; border-radius: 10px;">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<p class="card-title mb-0">Data Account</p>
|
|
<div>
|
|
<button class="btn btn-sm btn-outline-light" id="btnSelectMode"
|
|
style="font-weight:bold; border-radius:6px; padding: 6px 10px; font-size:16px;"
|
|
title="Select Multiple"><i class="mdi mdi-checkbox-multiple-marked-outline"></i></button>
|
|
<button class="btn btn-sm btn-danger ml-2" id="btnBulkDelete"
|
|
style="display:none; font-weight:bold; border-radius:6px; padding: 6px 10px; font-size:16px;"
|
|
title="Delete Selected"><i class="mdi mdi-delete"></i></button>
|
|
</div>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table class="table table-borderless" id="myTable">
|
|
<thead>
|
|
<tr>
|
|
<th>No</th>
|
|
<th>Name</th>
|
|
<th>Username</th>
|
|
<th style="width:80px; text-align:center;">Action</th>
|
|
</tr>
|
|
</thead>
|
|
<?php
|
|
$stmt = $pdo->query("SELECT * FROM account ORDER BY id_account ASC");
|
|
$accounts = $stmt->fetchAll();
|
|
?>
|
|
<tbody>
|
|
<?php
|
|
$no = 1;
|
|
foreach ($accounts as $row):
|
|
$sc = ($no % 2 == 1) ? 'row-odd' : 'row-even';
|
|
?>
|
|
<tr class="<?= $sc; ?>" id="row_<?= $row['id_account']; ?>">
|
|
<td style="vertical-align:middle; font-weight:600; color:#555;">
|
|
<span style="position: relative; display: inline-block;">
|
|
<?= $no++; ?>
|
|
<?php if(isset($_SESSION['user_id']) && $row['id_account'] == $_SESSION['user_id']): ?>
|
|
<i class="mdi mdi-star" style="color: #ffda07ff; font-size: 10px; position: absolute; top: -4px; right: -8px; line-height: 1;" title="Your Account"></i>
|
|
<?php endif; ?>
|
|
</span>
|
|
</td>
|
|
<td style="vertical-align:middle;">
|
|
<span style="font-weight:500; font-size:14px; color:#212529; display:inline-flex; align-items:center;"><?= htmlspecialchars($row['name']); ?></span>
|
|
</td>
|
|
<td style="vertical-align:middle; font-size:13px; color:#495057;">
|
|
<?= htmlspecialchars($row['username'] ?: '-'); ?>
|
|
</td>
|
|
<td style="vertical-align:middle; text-align:center; white-space:nowrap;">
|
|
<div class="d-flex align-items-center justify-content-center">
|
|
<div class="dropdown btn-row-delete">
|
|
<button class="btn btn-sm dropdown-dots-btn" type="button" data-toggle="dropdown"
|
|
aria-haspopup="true" aria-expanded="false" style="padding: 4px 8px;">
|
|
<i class="mdi mdi-dots-vertical"></i>
|
|
</button>
|
|
<div class="dropdown-menu dropdown-menu-right"
|
|
style="border-radius: 5px; box-shadow: 0 8px 24px rgba(0,0,0,0.08); border: 1px solid #f1f2f4; min-width: 150px; padding: 0; overflow: hidden;">
|
|
|
|
<button class="dropdown-item d-flex align-items-center py-3 px-4 btn-custom-dropdown" type="button"
|
|
onclick="editAccount('<?= $row['id_account']; ?>','<?= htmlspecialchars(addslashes($row['name'])); ?>','<?= htmlspecialchars(addslashes($row['username'])); ?>')">
|
|
<span style="font-size: 14px; font-weight: 400; color: #1f2125;">Edit Data</span>
|
|
</button>
|
|
<button class="dropdown-item d-flex align-items-center py-3 px-4 btn-custom-dropdown" type="button"
|
|
onclick="openDeleteModal('<?= $row['id_account']; ?>','<?= htmlspecialchars(addslashes($row['name'])); ?>')">
|
|
<span style="font-size: 14px; font-weight: 400; color: #ff4d4f;">Delete</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<input type="checkbox" class="bulk-cb" value="<?= $row['id_account']; ?>"
|
|
style="display:none; transform: scale(1.5); cursor:pointer; margin-left:10px; margin-top:0;">
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ==================== MODAL: DELETE ==================== -->
|
|
<div class="modal fade" id="modalDeleteAccount" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content mdl-card">
|
|
<form id="formDeleteAccount">
|
|
<div class="modal-body p-4" style="text-align: left;">
|
|
<input type="hidden" name="id_account" id="delete_id_account">
|
|
<h5 class="font-weight-bold mb-3">Delete Account</h5>
|
|
<p style="font-size:14px;color:#666;margin-bottom:20px;">Are you sure you want to delete <strong id="delete_account_name"></strong>?</p>
|
|
|
|
<div class="custom-control custom-checkbox mb-4" style="display:flex; align-items:center;">
|
|
<input type="checkbox" class="custom-control-input delete-confirm-cb" id="deleteConfirmCheckAccount" required>
|
|
<label class="custom-control-label" for="deleteConfirmCheckAccount" style="font-size:14px; color:#1e1f23; padding-top:2px;">I understand that all of my data will be deleted</label>
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-end" style="gap:8px;">
|
|
<button type="button" class="btn btn-light font-weight-bold" style="padding:8px 18px;font-size:13px;" data-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-danger font-weight-bold" style="padding:8px 18px;font-size:13px;" id="btnDeleteAccount" disabled>Delete</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
(function(){
|
|
var cb = document.getElementById('deleteConfirmCheckAccount');
|
|
var btn = document.getElementById('btnDeleteAccount');
|
|
if(cb && btn){
|
|
cb.addEventListener('change', function(){ btn.disabled = !this.checked; });
|
|
}
|
|
$('#modalDeleteAccount').on('hidden.bs.modal', function(){ if(cb){cb.checked=false;} if(btn){btn.disabled=true;} });
|
|
})();
|
|
</script>
|
|
|
|
<!-- ========================== CSS ========================== -->
|
|
<style>
|
|
.btn-custom-dashboard { background-color: #fff !important; border: 1px solid #e5e9f0 !important; color: #4b49ac !important; }
|
|
.btn-custom-dashboard:hover, .btn-custom-dashboard:focus, .btn-custom-dashboard:active { background-color: #f8f9fa !important; border: 1px solid #e5e9f0 !important; color: #4b49ac !important; box-shadow: none !important; }
|
|
.dropdown-dots-btn { background: transparent !important; border: none !important; box-shadow: none !important; }
|
|
.dropdown-dots-btn:hover, .dropdown-dots-btn:focus, .dropdown-dots-btn:active { background: transparent !important; border: none !important; box-shadow: none !important; outline: none !important; }
|
|
.dropdown-dots-btn i { font-size: 28px; color: #495057; }
|
|
#myTable thead th { border-bottom: 1px solid #e2e8f0 !important; }
|
|
#myTable tbody tr td { border-bottom: none !important; }
|
|
.mdl-card { border: none; border-radius: 6px; box-shadow: 0 8px 30px rgba(0, 0, 0, 0.1); }
|
|
.frm-lbl { font-size: 12px; font-weight: 600; color: #555; margin-bottom: 3px; }
|
|
.btn-custom-dropdown:hover, .btn-custom-dropdown:focus { background-color: #ededed !important; }
|
|
.delete-confirm-cb:checked ~ .custom-control-label::before { background-color: #ff4747 !important; border-color: #ff4747 !important; }
|
|
.row-highlight { background-color: #f1f5f9 !important; }
|
|
</style>
|
|
|
|
<?php include 'footer.php'; ?>
|
|
<?php include 'copy.php'; ?>
|
|
|
|
<!-- ========================== JS ========================== -->
|
|
<script>
|
|
var isEditing = false;
|
|
|
|
// Edit - populate form
|
|
function editAccount(id, name, username) {
|
|
if (isEditing && document.getElementById('input_id_account').value === id) {
|
|
cancelEdit();
|
|
return;
|
|
}
|
|
|
|
document.getElementById('input_id_account').value = id;
|
|
document.getElementById('input_name').value = name;
|
|
document.getElementById('input_username').value = username;
|
|
document.getElementById('input_password').value = '';
|
|
document.getElementById('input_password').required = false;
|
|
document.getElementById('input_password').readOnly = true;
|
|
|
|
document.getElementById('formTitle').innerText = 'Edit Account';
|
|
document.getElementById('formSubtitle').innerText = 'Update details for ' + name;
|
|
document.getElementById('btnCancelEdit').style.display = 'inline-block';
|
|
|
|
isEditing = true;
|
|
|
|
// Scroll to form
|
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
|
|
// Highlight the row being edited
|
|
document.querySelectorAll('#myTable tbody tr').forEach(function(tr) {
|
|
tr.classList.remove('row-highlight');
|
|
});
|
|
var editRow = document.getElementById('row_' + id);
|
|
if (editRow) {
|
|
editRow.classList.add('row-highlight');
|
|
}
|
|
}
|
|
|
|
// Cancel edit
|
|
function cancelEdit() {
|
|
document.getElementById('formAccount').reset();
|
|
document.getElementById('input_id_account').value = '';
|
|
document.getElementById('input_password').required = true;
|
|
document.getElementById('input_password').readOnly = false;
|
|
|
|
document.getElementById('formTitle').innerText = 'Add Account';
|
|
document.getElementById('formSubtitle').innerText = 'Create a new account.';
|
|
document.getElementById('btnCancelEdit').style.display = 'none';
|
|
isEditing = false;
|
|
|
|
// Remove row highlight
|
|
document.querySelectorAll('#myTable tbody tr').forEach(function(tr) {
|
|
tr.classList.remove('row-highlight');
|
|
});
|
|
}
|
|
|
|
// Save account
|
|
document.getElementById('formAccount').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
var btn = document.getElementById('btnSaveAccount');
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<i class="mdi mdi-loading mdi-spin mr-1"></i>Saving...';
|
|
|
|
var fd = new FormData(this);
|
|
|
|
fetch('account.php', { method: 'POST', body: fd })
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(d) {
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<i class="mdi mdi-content-save mr-1"></i>Save';
|
|
if (d.status === 'success') {
|
|
if (typeof showCustomAlert === 'function') showCustomAlert('Success', d.message, 'success');
|
|
setTimeout(function() { location.reload(); }, 1000);
|
|
} else {
|
|
if (typeof showCustomAlert === 'function') showCustomAlert('Failed', d.message || 'Failed to save data.', 'error');
|
|
else alert(d.message || 'Failed to save data.');
|
|
}
|
|
}).catch(function() {
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<i class="mdi mdi-content-save mr-1"></i>Save';
|
|
alert('Connection to server failed.');
|
|
});
|
|
});
|
|
|
|
function openDeleteModal(id, name) {
|
|
document.getElementById("delete_id_account").value = id;
|
|
document.getElementById("delete_account_name").innerText = name;
|
|
$("#modalDeleteAccount").modal("show");
|
|
}
|
|
|
|
$(document).ready(function () {
|
|
// Close dropdown on scroll
|
|
window.addEventListener('scroll', function() {
|
|
if ($('.dropdown-menu.show').length > 0) {
|
|
$('[data-toggle="dropdown"]').attr('aria-expanded', 'false');
|
|
$('.dropdown-menu').removeClass('show');
|
|
$('.dropdown').removeClass('show');
|
|
}
|
|
}, true);
|
|
|
|
window.isSelectMode = false;
|
|
$('#btnSelectMode').on('click', function () {
|
|
window.isSelectMode = !window.isSelectMode;
|
|
if (window.isSelectMode) {
|
|
$(this).html('<i class="mdi mdi-close"></i>').removeClass('btn-outline-secondary').addClass('btn-light').attr('title', 'Cancel Selection');
|
|
$('#btnBulkDelete').css('display', 'inline-block');
|
|
$('.btn-row-delete').hide();
|
|
$('.bulk-cb').css('display', 'inline-block').prop('checked', false);
|
|
} else {
|
|
$(this).html('<i class="mdi mdi-checkbox-multiple-marked-outline"></i>').removeClass('btn-light').addClass('btn-outline-secondary').attr('title', 'Select Multiple');
|
|
$('#btnBulkDelete').hide();
|
|
$('.btn-row-delete').css('display', 'inline-block');
|
|
$('.bulk-cb').hide().prop('checked', false);
|
|
}
|
|
});
|
|
|
|
$('#btnBulkDelete').on('click', function () {
|
|
const selected = $('.bulk-cb:checked').map(function () { return $(this).val(); }).get();
|
|
if (selected.length === 0) {
|
|
if (typeof showCustomAlert === 'function') showCustomAlert('Please select at least one item to delete!', 'warning');
|
|
else alert('Please select at least one item to delete!');
|
|
return;
|
|
}
|
|
window.bulkDeleteIds = selected;
|
|
document.getElementById("delete_id_account").value = "BULK";
|
|
document.getElementById("delete_account_name").innerText = selected.length + " items selected";
|
|
$("#modalDeleteAccount").modal("show");
|
|
});
|
|
|
|
$("#formDeleteAccount").submit(function (e) {
|
|
e.preventDefault();
|
|
var val = $("#delete_id_account").val();
|
|
if (val === "BULK") {
|
|
$("#btnDeleteAccount").prop("disabled", true);
|
|
$("#btnDeleteAccount").html('<i class="mdi mdi-loading mdi-spin mr-1"></i>Deleting...');
|
|
let promises = window.bulkDeleteIds.map(id => {
|
|
let fd = new FormData();
|
|
fd.append('id_account', id);
|
|
return fetch('delete_account.php', { method: 'POST', body: fd }).then(r => r.json());
|
|
});
|
|
Promise.all(promises).then(() => {
|
|
setTimeout(function () { $("#modalDeleteAccount").modal("hide"); location.reload(); }, 1200);
|
|
});
|
|
} else {
|
|
$.post("delete_account.php", $(this).serialize(), function (d) {
|
|
if (d.status === "success") {
|
|
$("#btnDeleteAccount").prop("disabled", true);
|
|
setTimeout(function () { $("#modalDeleteAccount").modal("hide"); location.reload(); }, 1200);
|
|
} else {
|
|
if (typeof showCustomAlert === 'function') showCustomAlert(d.message);
|
|
else alert(d.message);
|
|
}
|
|
}, "json");
|
|
}
|
|
});
|
|
|
|
if ($.fn.DataTable.isDataTable('#myTable')) {
|
|
$('#myTable').DataTable().destroy();
|
|
}
|
|
var table = $('#myTable').DataTable({
|
|
"columnDefs": [{ "orderable": false, "targets": 3 }, { "width": "40px", "targets": 3 }],
|
|
"order": [[0, 'asc']]
|
|
});
|
|
|
|
// Eye Toggle Logic
|
|
$('.eye-toggle').on('click', function () {
|
|
var targetId = $(this).data('target');
|
|
var input = $('#' + targetId);
|
|
var icon = $(this).find('i');
|
|
|
|
if (input.attr('type') === 'password') {
|
|
input.attr('type', 'text');
|
|
icon.removeClass('mdi-eye-off').addClass('mdi-eye');
|
|
} else {
|
|
input.attr('type', 'password');
|
|
icon.removeClass('mdi-eye').addClass('mdi-eye-off');
|
|
}
|
|
});
|
|
|
|
// Make password required for new records
|
|
document.getElementById('input_password').required = true;
|
|
});
|
|
</script>
|