TKK_E32230273/account.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>