454 lines
20 KiB
PHP
454 lines
20 KiB
PHP
<?php
|
|
include 'koneksi.php';
|
|
|
|
// Create table if not exists
|
|
$pdo->exec("CREATE TABLE IF NOT EXISTS room_restrictions (
|
|
id_restriction INT AUTO_INCREMENT PRIMARY KEY,
|
|
day_of_week VARCHAR(20) NOT NULL,
|
|
time_from TIME NOT NULL,
|
|
time_to TIME NOT NULL,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
|
|
|
// Fetch all restrictions
|
|
$stmt = $pdo->query("SELECT * FROM room_restrictions ORDER BY FIELD(day_of_week, 'Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday','Every Day'), time_from ASC");
|
|
$restrictions = $stmt->fetchAll();
|
|
|
|
include 'header.php';
|
|
?>
|
|
<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">Room</h3>
|
|
<h6 class="font-weight-normal mb-0">Manage room access restrictions, <span class="text-primary">Set restricted hours for the room!</span></h6>
|
|
</div>
|
|
|
|
<div class="col-12 col-xl-4">
|
|
<div class="justify-content-end d-flex flex-wrap" style="gap:15px; align-items:center;">
|
|
<div class="justify-content-end d-flex">
|
|
</div>
|
|
</div>
|
|
</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">Add Restriction</p>
|
|
<p class="text-muted mt-1 mb-4" style="font-size:12px;">Define the time period when the room cannot be accessed via fingerprint.</p>
|
|
|
|
<form id="formRestriction">
|
|
<input type="hidden" name="id_restriction" id="edit_id_restriction" value="">
|
|
<div class="row">
|
|
<!-- Day Selection -->
|
|
<div class="col-md-4 mb-3">
|
|
<label class="frm-lbl">Day</label>
|
|
<select name="day_of_week" id="input_day" class="form-control form-control-sm" required style="border-color:#e5e9f0; height:38px;">
|
|
<option value="">--- Select Day ---</option>
|
|
<option value="Monday">Monday</option>
|
|
<option value="Tuesday">Tuesday</option>
|
|
<option value="Wednesday">Wednesday</option>
|
|
<option value="Thursday">Thursday</option>
|
|
<option value="Friday">Friday</option>
|
|
<option value="Saturday">Saturday</option>
|
|
<option value="Sunday">Sunday</option>
|
|
<option value="Every Day">Every Day (*)</option>
|
|
</select>
|
|
</div>
|
|
<!-- From Time -->
|
|
<div class="col-md-4 mb-3">
|
|
<label class="frm-lbl">From</label>
|
|
<input type="hidden" name="time_from" id="input_time_from">
|
|
<div class="clock-picker-group form-control form-control-sm" style="border-color:#e5e9f0; height:38px; padding: 0 10px;">
|
|
<select id="from_hour" class="clock-picker-select" required>
|
|
<option value="">--</option>
|
|
<?php for($h=0;$h<=23;$h++): ?>
|
|
<option value="<?= str_pad($h,2,'0',STR_PAD_LEFT) ?>"><?= str_pad($h,2,'0',STR_PAD_LEFT) ?></option>
|
|
<?php endfor; ?>
|
|
</select>
|
|
<span class="clock-picker-separator">:</span>
|
|
<select id="from_minute" class="clock-picker-select" required>
|
|
<option value="">--</option>
|
|
<?php for($m=0;$m<60;$m++): ?>
|
|
<option value="<?= str_pad($m,2,'0',STR_PAD_LEFT) ?>"><?= str_pad($m,2,'0',STR_PAD_LEFT) ?></option>
|
|
<?php endfor; ?>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<!-- To Time -->
|
|
<div class="col-md-4 mb-3">
|
|
<label class="frm-lbl">To</label>
|
|
<input type="hidden" name="time_to" id="input_time_to">
|
|
<div class="clock-picker-group form-control form-control-sm" style="border-color:#e5e9f0; height:38px; padding: 0 10px;">
|
|
<select id="to_hour" class="clock-picker-select" required>
|
|
<option value="">--</option>
|
|
<?php for($h=0;$h<=23;$h++): ?>
|
|
<option value="<?= str_pad($h,2,'0',STR_PAD_LEFT) ?>"><?= str_pad($h,2,'0',STR_PAD_LEFT) ?></option>
|
|
<?php endfor; ?>
|
|
</select>
|
|
<span class="clock-picker-separator">:</span>
|
|
<select id="to_minute" class="clock-picker-select" required>
|
|
<option value="">--</option>
|
|
<?php for($m=0;$m<60;$m++): ?>
|
|
<option value="<?= str_pad($m,2,'0',STR_PAD_LEFT) ?>"><?= str_pad($m,2,'0',STR_PAD_LEFT) ?></option>
|
|
<?php endfor; ?>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="d-flex align-items-center" style="gap:10px;">
|
|
<button type="submit" class="btn btn-primary btn-sm" id="btnSaveRestriction" style="padding:8px 20px; font-weight:600;">
|
|
<i class="mdi mdi-content-save mr-1"></i>Save
|
|
</button>
|
|
<button type="button" class="btn btn-light btn-sm" id="btnCancelEdit" style="display:none; padding:8px 20px; font-weight:600;" onclick="cancelEdit()">
|
|
Cancel
|
|
</button>
|
|
</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 #e5e9f0;box-shadow:none;border-radius:12px;">
|
|
<div class="card-body" style="padding:28px;">
|
|
<p class="card-title mb-3">Restricted Hours</p>
|
|
<div class="table-responsive">
|
|
<table class="table table-borderless" id="restrictionTable">
|
|
<thead>
|
|
<tr>
|
|
<th style="width:50px;">No</th>
|
|
<th>Day</th>
|
|
<th>From</th>
|
|
<th>To</th>
|
|
<th style="width:180px; text-align:center;">Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php if (empty($restrictions)): ?>
|
|
<tr id="emptyRow">
|
|
<td colspan="5" style="text-align:center; padding:30px 10px; color:#999; font-size:13px;">
|
|
<i class="mdi mdi-clock-alert-outline" style="font-size:32px; display:block; margin-bottom:6px; color:#ddd;"></i>
|
|
No restrictions have been set yet
|
|
</td>
|
|
</tr>
|
|
<?php else:
|
|
$no = 1;
|
|
foreach ($restrictions as $row):
|
|
$sc = ($no % 2 == 1) ? 'row-odd' : 'row-even';
|
|
$dayLabel = $row['day_of_week'];
|
|
if ($dayLabel === 'Every Day') $dayLabel = 'Every Day (*)';
|
|
?>
|
|
<tr class="<?= $sc; ?>" id="row_<?= $row['id_restriction']; ?>">
|
|
<td style="vertical-align:middle; font-weight:600; color:#555;"><?= $no++; ?></td>
|
|
<td style="vertical-align:middle;">
|
|
<span style="font-weight:500; font-size:14px; color:#212529;"><?= htmlspecialchars($dayLabel); ?></span>
|
|
</td>
|
|
<td style="vertical-align:middle; font-weight:500;">
|
|
<span style="font-family:'Courier New',monospace; font-size:13px; color:#4b49ac; font-weight:bold;"><?= substr($row['time_from'], 0, 5); ?></span>
|
|
</td>
|
|
<td style="vertical-align:middle; font-weight:500;">
|
|
<span style="font-family:'Courier New',monospace; font-size:13px; color:#4b49ac; font-weight:bold;"><?= substr($row['time_to'], 0, 5); ?></span>
|
|
</td>
|
|
<td style="vertical-align:middle; text-align:center;">
|
|
<button class="btn-edit-action btn-edit-row" id="btnEdit_<?= $row['id_restriction']; ?>" title="Edit" onclick="editRestriction('<?= $row['id_restriction']; ?>', '<?= htmlspecialchars($row['day_of_week']); ?>', '<?= substr($row['time_from'], 0, 5); ?>', '<?= substr($row['time_to'], 0, 5); ?>')">
|
|
<i class="mdi mdi-pencil mr-1"></i>Edit
|
|
</button>
|
|
<button class="btn-delete-action ml-2" title="Delete" onclick="openDeleteModal('<?= $row['id_restriction']; ?>', '<?= htmlspecialchars($row['day_of_week']); ?>')">
|
|
<i class="mdi mdi-delete"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
<?php
|
|
endforeach;
|
|
endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ==================== MODAL: DELETE ==================== -->
|
|
<div class="modal fade" id="modalDeleteRestriction" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content mdl-card">
|
|
<form id="formDeleteRestriction">
|
|
<div class="modal-body p-4" style="text-align: left;">
|
|
<input type="hidden" name="id_restriction" id="delete_id_restriction">
|
|
<h5 class="font-weight-bold mb-3">Delete Restriction</h5>
|
|
<p style="font-size:14px;color:#666;margin-bottom:20px;">Are you sure you want to delete the restriction for <strong id="delete_restriction_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="deleteConfirmCheckRestriction" required>
|
|
<label class="custom-control-label" for="deleteConfirmCheckRestriction" style="font-size:14px; color:#1e1f23; padding-top:2px;">I understand that this restriction will be permanently removed</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="btnDeleteRestriction" disabled>Delete</button>
|
|
</div>
|
|
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
(function(){
|
|
var cb = document.getElementById('deleteConfirmCheckRestriction');
|
|
var btn = document.getElementById('btnDeleteRestriction');
|
|
if(cb && btn){
|
|
cb.addEventListener('change', function(){ btn.disabled = !this.checked; });
|
|
}
|
|
$('#modalDeleteRestriction').on('hidden.bs.modal', function(){ if(cb){cb.checked=false;} if(btn){btn.disabled=true;} });
|
|
})();
|
|
</script>
|
|
|
|
<!-- ========================== CSS ========================== -->
|
|
<style>
|
|
/* ── TABLE ── */
|
|
#restrictionTable { border-collapse: collapse; width: 100%; margin-top: 10px; }
|
|
#restrictionTable thead th { border-bottom: 1px solid #e2e8f0 !important; font-size: 13px; font-weight: 600; color: #555; text-transform: uppercase; letter-spacing: 0.5px; padding: 20px 24px !important; }
|
|
#restrictionTable tbody tr td { border-bottom: none !important; padding: 20px 24px !important; }
|
|
|
|
#restrictionTable tbody tr.row-odd > td { background-color: #fafafa !important; }
|
|
#restrictionTable tbody tr.row-even > td { background-color: #ffffff !important; }
|
|
|
|
#restrictionTable tbody tr.row-highlight td { background-color: #f0f4ff !important; }
|
|
|
|
.frm-lbl { font-size: 12px; font-weight: 600; color: #555; margin-bottom: 3px; display: block; }
|
|
|
|
/* ── CLOCK PICKER ── */
|
|
.clock-picker-group {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
}
|
|
.clock-picker-select {
|
|
height: 28px;
|
|
width: 44px;
|
|
border: none;
|
|
background: transparent;
|
|
font-size: 14px;
|
|
font-weight: 400;
|
|
color: #495057;
|
|
text-align: center;
|
|
cursor: pointer;
|
|
outline: none;
|
|
-webkit-appearance: none;
|
|
-moz-appearance: none;
|
|
appearance: none;
|
|
padding: 0 2px;
|
|
}
|
|
.clock-picker-select:focus {
|
|
background: transparent;
|
|
}
|
|
.clock-picker-separator {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: #495057;
|
|
line-height: 28px;
|
|
}
|
|
|
|
/* ── MODALS ── */
|
|
.mdl-card { border: none; border-radius: 6px; box-shadow: 0 8px 30px rgba(0,0,0,0.1); }
|
|
|
|
.delete-confirm-cb:checked ~ .custom-control-label::before {
|
|
background-color: #ff4747 !important;
|
|
border-color: #ff4747 !important;
|
|
}
|
|
|
|
.btn-edit-action {
|
|
font-size: 13px; font-weight: 500; color: #333; background: #fff;
|
|
border: 1px solid #d4d4d8; border-radius: 6px; padding: 6px 16px;
|
|
cursor: pointer; transition: all .15s; display: inline-flex; align-items: center;
|
|
line-height: 1;
|
|
}
|
|
.btn-edit-action:hover { background: #f5f5f5; border-color: #bbb; color: #333; text-decoration: none; }
|
|
|
|
.btn-delete-action {
|
|
font-size: 13px; font-weight: 500; color: #fff; background: #ff4747;
|
|
border: 1px solid #ff4747; border-radius: 6px; padding: 6px 16px;
|
|
cursor: pointer; transition: all .15s; display: inline-flex; align-items: center;
|
|
line-height: 1;
|
|
}
|
|
.btn-delete-action:hover { background: #e03e3e; border-color: #e03e3e; color: #fff; text-decoration: none; }
|
|
|
|
</style>
|
|
|
|
<!-- content-wrapper ends -->
|
|
<?php include 'footer.php'; ?>
|
|
<?php include 'copy.php'; ?>
|
|
|
|
<!-- ========================== JS ========================== -->
|
|
<script>
|
|
var isEditing = false;
|
|
|
|
// ── CLOCK PICKER HELPERS ──
|
|
function syncClockToHidden(prefix) {
|
|
var h = document.getElementById(prefix + '_hour').value;
|
|
var m = document.getElementById(prefix + '_minute').value;
|
|
if (!h || !m) { document.getElementById('input_time_' + prefix).value = ''; return; }
|
|
document.getElementById('input_time_' + prefix).value = String(h).padStart(2,'0') + ':' + m;
|
|
}
|
|
|
|
function set24hToClock(prefix, time24) {
|
|
if (!time24) return;
|
|
var parts = time24.split(':');
|
|
document.getElementById(prefix + '_hour').value = parts[0];
|
|
document.getElementById(prefix + '_minute').value = parts[1];
|
|
syncClockToHidden(prefix);
|
|
}
|
|
|
|
// Attach change listeners to sync hidden fields
|
|
['from', 'to'].forEach(function(prefix) {
|
|
['_hour', '_minute'].forEach(function(suffix) {
|
|
document.getElementById(prefix + suffix).addEventListener('change', function() {
|
|
syncClockToHidden(prefix);
|
|
});
|
|
});
|
|
});
|
|
|
|
// Save / Update restriction
|
|
document.getElementById('formRestriction').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
// Sync clock pickers to hidden inputs
|
|
syncClockToHidden('from');
|
|
syncClockToHidden('to');
|
|
|
|
var timeFrom = document.getElementById('input_time_from').value;
|
|
var timeTo = document.getElementById('input_time_to').value;
|
|
|
|
if (!timeFrom || !timeTo) {
|
|
if (typeof showCustomAlert === 'function') showCustomAlert('Incomplete', 'Please select both From and To times.', 'error');
|
|
else alert('Please select both From and To times.');
|
|
return;
|
|
}
|
|
|
|
var btn = document.getElementById('btnSaveRestriction');
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<i class="mdi mdi-loading mdi-spin mr-1"></i>Saving...';
|
|
|
|
var fd = new FormData(this);
|
|
var url = fd.get('id_restriction') ? 'update_room_restriction.php' : 'save_room_restriction.php';
|
|
|
|
fetch(url, { 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.');
|
|
});
|
|
});
|
|
|
|
// Edit - populate form
|
|
function editRestriction(id, day, timeFrom, timeTo) {
|
|
// If already editing this row, cancel
|
|
if (isEditing && document.getElementById('edit_id_restriction').value === id) {
|
|
cancelEdit();
|
|
return;
|
|
}
|
|
|
|
document.getElementById('edit_id_restriction').value = id;
|
|
document.getElementById('input_day').value = day;
|
|
|
|
// Populate clock pickers from 24h values
|
|
set24hToClock('from', timeFrom);
|
|
set24hToClock('to', timeTo);
|
|
|
|
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('#restrictionTable 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('edit_id_restriction').value = '';
|
|
document.getElementById('input_day').value = '';
|
|
document.getElementById('input_time_from').value = '';
|
|
document.getElementById('input_time_to').value = '';
|
|
// Reset clock pickers
|
|
['from', 'to'].forEach(function(prefix) {
|
|
document.getElementById(prefix + '_hour').value = '';
|
|
document.getElementById(prefix + '_minute').value = '';
|
|
});
|
|
document.getElementById('btnCancelEdit').style.display = 'none';
|
|
isEditing = false;
|
|
|
|
// Remove row highlight
|
|
document.querySelectorAll('#restrictionTable tbody tr').forEach(function(tr) {
|
|
tr.classList.remove('row-highlight');
|
|
});
|
|
}
|
|
|
|
// Delete
|
|
function openDeleteModal(id, dayName) {
|
|
document.getElementById('delete_id_restriction').value = id;
|
|
document.getElementById('delete_restriction_name').innerText = dayName;
|
|
$('#modalDeleteRestriction').modal('show');
|
|
}
|
|
|
|
document.getElementById('formDeleteRestriction').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
var btn = document.getElementById('btnDeleteRestriction');
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<i class="mdi mdi-loading mdi-spin mr-1"></i>Deleting...';
|
|
|
|
var fd = new FormData(this);
|
|
fetch('delete_room_restriction.php', { method: 'POST', body: fd })
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(d) {
|
|
btn.disabled = false;
|
|
btn.innerHTML = 'Delete';
|
|
if (d.status === 'success') {
|
|
$('#modalDeleteRestriction').modal('hide');
|
|
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 delete.', 'error');
|
|
else alert(d.message || 'Failed to delete.');
|
|
}
|
|
}).catch(function() {
|
|
btn.disabled = false;
|
|
btn.innerHTML = 'Delete';
|
|
alert('Connection to server failed.');
|
|
});
|
|
});
|
|
</script>
|